题目描述
难度分:1756
输入n,m,q(1≤n,m,q≤2×105)。
有一个n行m列的矩阵,初始值均为0。
然后输入q个操作/询问,格式如下:
1 L R x
:把矩阵第L,L+1,…,R列的所有元素都加上x(1≤x≤109)。2 i x
:把矩阵第i行的元素全部替换为x(1≤x≤109)。3 i j
:输出矩阵第i行第j列的元素值。
矩阵行列编号均从1开始。
输入样例1
3 3 9
1 1 2 1
3 2 2
2 3 2
3 3 3
3 3 1
1 2 3 3
3 3 2
3 2 3
3 1 2
输出样例1
1
2
2
5
3
4
输入样例2
1 1 10
1 1 1 1000000000
1 1 1 1000000000
1 1 1 1000000000
1 1 1 1000000000
1 1 1 1000000000
1 1 1 1000000000
1 1 1 1000000000
1 1 1 1000000000
1 1 1 1000000000
3 1 1
输出样例2
9000000000
输入样例3
10 10 10
1 1 8 5
2 2 6
3 2 1
3 4 7
1 5 9 7
3 3 2
3 2 8
2 8 10
3 8 8
3 1 10
输出样例3
6
5
5
13
10
0
算法
离线+树状数组
刚开始题读假了,没注意到n≤200000,m≤200000,上了二维线段树,结果一直RE
。后来反应过来之后想了好久也没想到什么数据结构可以无脑做,但是离线做的话有点思路。
我们可以维护一个长度为O(m)的树状数组BIT,用来存操作1作用下每列的和。本题是区间加,单点查询,因此树状数组维护的是一个差分数组。
先遍历一遍所有的询问queries,对于每个询问index,当遇到操作2时维护一个last数组,last[i]=index表示第i行在第index条询问时做了一次操作2。当遇到操作3时,维护一个二维数组path,将index追加到path[last[i]]中,这样一来path[last[i]]中就是第i行操作2(询问编号是last[i])后面所跟上的操作3编号。
最后再遍历一遍queries数组求答案:
- 如果遇到的是操作1,直接在树状数组实现的差分数组BIT上进行区间加法,在[l,r]上加x。
- 如果遇到的是操作2,遍历index后面紧接着的操作3(即path[index])编号idx,把x−BIT.query(queries[idx].j)存入到ans[idx]中。
- 如果遇到的是操作3,输出BIT.query(j)+ans[index]。前一项是迄今为止所有操作1作用在单元格(i,j)上的结果,第二项中有最近一次的x,所以我们只需要把“最近一次操作2之前所有操作的贡献”减去就能得到答案。而这个贡献就是情况2中x后面减去的那一项,这是因为在真正遍历到这个操作3之前,通过情况2遍历得到的BIT.query(queries[idx].j)值只是那次操作2之前所有操作的贡献。
复杂度分析
时间复杂度
遍历queries的时间复杂度为O(q)。在遍历所有询问的内部,也存在遍历path数组的操作,path数组中每个元素只会被遍历一次,元素数量是O(q)级别。而处理每条询问的过程中,会对长度在O(m)级别的树状数组进行区间加和单点查询操作,时间复杂度为O(log2m)。
综上,整个算法的时间复杂度为O(qlog2m)。
空间复杂度
ans数组、path数组、last数组的空间消耗均为O(q),树状数组的空间消耗为O(m)。因此,整个算法的额外空间复杂度为O(m+q)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 200010;
int n, m, q, last[N];
LL ans[N];
vector<int> path[N];
class Fenwick {
public:
explicit Fenwick(int n): sums_(n + 1) {}
int lowbit(int x) {
return x&-x;
}
void add(int idx, int val) {
for(; idx < sums_.size(); idx += lowbit(idx)) {
sums_[idx] += val;
}
}
LL query(int idx) {
LL ans = 0;
for(; idx > 0; idx -= lowbit(idx)) {
ans += sums_[idx];
}
return ans;
}
LL query(int left, int right) {
return query(right) - query(left - 1);
}
private:
vector<LL> sums_;
};
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> q;
vector<vector<int>> queries;
queries.push_back({});
for(int index = 1; index <= q; index++) {
int op;
cin >> op;
if(op == 1) {
int l, r, x;
cin >> l >> r >> x;
queries.push_back({op, l, r, x});
}else if(op == 2) {
int i, x;
cin >> i >> x;
last[i] = index;
queries.push_back({op, i, x});
}else {
int i, j;
cin >> i >> j;
queries.push_back({op, i, j});
path[last[i]].push_back(index);
}
}
Fenwick bit(m + 5);
for(int index = 1; index <= q; index++) {
int op = queries[index][0];
if(op == 1) {
int l = queries[index][1], r = queries[index][2], x = queries[index][3];
bit.add(l, x);
bit.add(r + 1, -x);
}else if(op == 2) {
int i = queries[index][1], x = queries[index][2];
for(int idx: path[index]) {
ans[idx] = x - bit.query(queries[idx][2]);
}
}else {
int i = queries[index][1], j = queries[index][2];
cout << ans[index] + bit.query(j) << endl;
}
}
return 0;
}