https://atcoder.jp/contests/jsc2021/tasks/jsc2021_f
前提知識
- BIT
- 座標圧縮
解説
https://atcoder.jp/contests/jsc2021/submissions/21832371
クエリ問題に対する取り組み方というのはそれほど多くない。
今回の問題は差分計算をする問題である。
差分計算
q番目のクエリを処理した答えがansであるときに、q+1番目のクエリの答えを計算する。
差分計算方法の1つは、「変更前の計算を引く」→「変更後の計算を追加する」である。
クエリ1とクエリ2はどちらも修正方法は全く変わりないので、片方だけ紹介する。
クエリ1
a[X[i]]をY[i]に書き換える。
[i]を書くのが面倒なので、x,yと書くことにする。
まず、もともとのa[x]に入っている値がansにどのような影響を与えているかを考える。
配列bとのmaxを掛け合わせて総和を取っている部分が対象となるが、
- a[x]以下
- a[x]より大きい
の2つの場合に分けて考えることができる。
a[x]以下
配列の値がa[x]以下であれば、max(b,a[x])=a[x]となる。
なので、ansにa[x] * (配列bの中でa[x]以下の個数)が入っていることになる。
これをansから引こう。
この個数を計算するにはBITを使うのがよい。
Bcnt[x] := 数xの個数
としておいて、区間総和をBITで取る。
a[x]より大きい
配列の値がa[x]より大きいなら、max(b,a[x])=bとなる。
なので、ansには(配列bの中でa[x]より大きい数)の総和が入っていることになる。
これもansから引こう。
Btot[x] := 数xの総和(=数xの個数×x)
としておいて、これも区間総和をBITで取ればいい。
この2つの場合について、変更前のansからこれらの場合の数を引いて、a[x]=yと更新をして、再度同様の計算をしてその数を足してやれば
新しいansを計算することができる。
実装
ここまでの思考の流れが分かっていれば十分である。
最後の問題は実装である。
Bcnt[x]のように数を添え字に入れるが、Yの上限を見ると108であるので普通に作るとメモリが足りない。
よって、クエリをすべて先読みして、座標圧縮をすることでここは解決できる。
平衡二分木を使えば殴れるが、どっちもどっちな感じがする。
int N, M, Q, T[201010], X[201010], Y[201010]; int A[201010], B[201010]; BIT<ll> Acnt(201010), Bcnt(201010); BIT<ll> Atot(201010), Btot(201010); //--------------------------------------------------------------------------------------------------- void _main() { cin >> N >> M >> Q; rep(i, 0, Q) cin >> T[i] >> X[i] >> Y[i]; auto ZY = compress1(Y, Q + 1); Acnt.add(0, N); Bcnt.add(0, M); ll ans = 0; rep(q, 0, Q) { if (T[q] == 1) { int x = X[q] - 1; int y = Y[q]; ans -= Bcnt.get(0, A[x] + 1) * ZY[A[x]]; ans -= Btot.get(A[x] + 1, 201010); Acnt.add(A[x], -1); Atot.add(A[x], -ZY[A[x]]); A[x] = y; ans += Bcnt.get(0, A[x] + 1) * ZY[A[x]]; ans += Btot.get(A[x] + 1, 201010); Acnt.add(A[x], 1); Atot.add(A[x], ZY[A[x]]); } else { int x = X[q] - 1; int y = Y[q]; ans -= Acnt.get(0, B[x] + 1) * ZY[B[x]]; ans -= Atot.get(B[x] + 1, 201010); Bcnt.add(B[x], -1); Btot.add(B[x], -ZY[B[x]]); B[x] = y; ans += Acnt.get(0, B[x] + 1) * ZY[B[x]]; ans += Atot.get(B[x] + 1, 201010); Bcnt.add(B[x], 1); Btot.add(B[x], ZY[B[x]]); } printf("%lld\n", ans); } }