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
using namespace std;
#define LL long long
struct fwt{
LL n;
vector 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 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;
}