1. 程式人生 > 其它 >NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)

NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)

比賽連結:

https://atcoder.jp/contests/abc253

F - Operations on a Matrix

題意:

\(n * m\) 的初始值全為 0 的矩陣,\(q\) 次詢問,有三種操作。
操作 1:l 到 \(r\) 所有列的所有元素都加上 \(x\)
操作 2:將第 \(i\) 列所有元素都替換成 \(x\)
操作 3:查詢 \((i, j)\) 的元素值。

思路:

行列分開來考慮,對於列,可以通過樹狀陣列維護一個差分陣列去計算。
對於行,對這個位置有影響的只有最近的一次操作 2,每次將最近的一次操作 2 記錄下來,然後查詢的時候進行修改。
查詢的位置的答案就是改行最近一次操作 2 的賦值 + 在這次操作之後所有對該位置有影響的操作 1 的值。
\(ans_{i,j} = r + s_j - s_r\)


\(ans_{i,j}\) 就是最後的答案。
\(r\) 是查詢的這一行最近的一次操作 2。
\(s_j\) 表示到當前這一步所有操作的第 \(i\) 列的值。
\(s_r\) 表示到查詢的這一行最近的一次操作 2 的所有操作的第 \(i\) 列的值。
可以考慮每一個操作 2 對後面查詢值的影響,在讀入時記錄下每個操作 2 的影響,然後再通過一個樹狀陣列去求最終的答案。
即先求出 \(r + s_j\) 然後再逐個減去 \(s_r\)

程式碼:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
struct fwt{
	LL n;
	vector <LL> a;
	fwt(LL n) : n(n), a(n + 1) {}
	LL sum(LL x){
		LL res = 0;
		for (; x; x -= x & -x)
			res += a[x];
		return res;
	}
	void add(LL x, LL k){
		for (; x <= n; x += x & -x)
			a[x] += k;
	}
	LL query(LL x, LL y){
		return sum(y) - sum(x - 1);
	}
};
LL n, m, q;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin >> n >> m >> q;
	vector <LL> op(q), l(q), r(q), x(q), ans(q);
	vector < vector < pair < LL, LL> > > sub(q);
	vector < pair < LL, LL> > last(n + 1);
	fwt f(m);
	for (int i = 0; i < q; i ++ ){
		cin >> op[i];
		if (op[i] == 1){
			cin >> l[i] >> r[i] >> x[i];
			f.add(l[i], x[i]);
			f.add(r[i] + 1, -x[i]);
		}
		else if (op[i] == 2){
			cin >> r[i] >> x[i];
			last[r[i]] = {i, x[i]};
		}
		else{
			cin >> l[i] >> r[i];
			auto [p, num] = last[l[i]];
			ans[i] = num + f.sum(r[i]);
			sub[p].push_back({i, r[i]});
		}
	}
	fwt t(m);
	for (int i = 0; i < q; i ++ ){
		for (auto [p, c] : sub[i])
			ans[p] -= t.sum(c);
		if (op[i] == 1){
			t.add(l[i], x[i]);
			t.add(r[i] + 1, -x[i]);
		}
		if (op[i] == 3)
			cout << ans[i] << "\n";
	}
	return 0;
}