描述:
维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.
思路:
三维,时间一维,x轴一维,y轴一维,
按照一维排序之后,然后用左区间更新右区间;树状数组进行维护.
这里有两种写法,
一种是 用时间分治,一种是用 x 轴分治,
用时间分治:
这种用时间分治的方法,在CDQ中不需要在排序了, 这样子省下了不少时间.
为什么不需要排序?
我们先按照x轴排序,时间在区间里是.乱序的,然后时间在 [l,mid] 中的算左区间,[mid+1, r] 的算右区间.
区间 从 l 到 r ,那么时间也是从 l 到 r, 当我们递归每一层的时候,要把时间和 l , r 对应上.
#include<bits/stdc++.h>
#define low(x) (x & (-x))
using namespace std;
const int N = 2e5+100;
const int M = 2e6+10;
struct node{
int id,qid,x,y,c;
}f[N*4],g[N*4];
bool cmp(node A, node B){
if (A.x != B.x) return A.x < B.x;
if (A.y != B.y) return A.y < B.y;
return A.id < B.id;
}
int n,m,s,tim,qtim,ans[N];
int c[M];
void add(int x, int y){
for (; x <= m; x += low(x)) c[x] += y;
}
int sum(int x){
int ans = 0;
for (; x; x -= low(x)) ans += c[x];
return ans;
}
void CDQ(int l, int r){
if (l >= r) return;
int mid = (l + r) >> 1;
for (int i = l; i <= r; ++i){
if (f[i].id <= mid && f[i].qid == -1) add(f[i].y,f[i].c); else
if (f[i].id > mid && f[i].qid != -1) ans[f[i].qid] += f[i].c*sum(f[i].y);
}
for (int i = l; i <= r; ++i)
if (f[i].id <= mid && f[i].qid == -1) add(f[i].y,-f[i].c);
int x = l, y = mid;
for (int i = l; i <= r; ++i)
if (f[i].id <= mid) g[x++] = f[i]; else g[++y] = f[i];
for (int i = l; i <= r; ++i)
f[i] = g[i];
CDQ(l,mid); CDQ(mid + 1, r);
}
int main(){
scanf("%d%d",&s,&m);
int op,x,y,xx,yy,z;
while(scanf("%d",&op)){
if (op == 3) break;
if (op == 1){
scanf("%d%d%d",&x,&y,&z);
f[++tim] = (node){tim,-1,x,y,z};
} else{
scanf("%d%d%d%d",&x,&y,&xx,&yy);
f[++tim] = (node){tim,qtim,xx,yy,1};
f[++tim] = (node){tim,qtim,x-1,yy,-1};
f[++tim] = (node){tim,qtim,xx,y-1,-1};
f[++tim] = (node){tim,qtim++,x-1,y-1,1};
}
}
sort(f+1,f+tim+1,cmp);
CDQ(1,tim);
for (int i = 0; i < qtim; ++i)
printf("%d\n",ans[i]);
return 0;
}
/*
0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
*/
第二种写法实现就时间长了一点,
因为在 cdq中排序了,在 cdq 中,我们就是 [l,mid] 是左区间, [mid+1,r] 是右区间.用左区间去更新右区间,
然后这个时候是有序的,我们要以 x 轴为关键字排序,然后打乱区间.再进行更新.
#include<bits/stdc++.h>
#define low(x) (x & (-x))
using namespace std;
const int N = 2e5+100;
const int M = 2e6+10;
struct node{
int id,qid,x,y,c;
bool vis;
}f[N*4],g[N*4];
bool cmp(node A, node B){
if (A.x != B.x) return A.x < B.x;
if (A.y != B.y) return A.y < B.y;
return A.vis < B.vis;
}
int n,m,s,tim,qtim,ans[N];
int c[M];
void add(int x, int y){
for (; x <= m; x += low(x)) c[x] += y;
}
int sum(int x){
int ans = 0;
for (; x; x -= low(x)) ans += c[x];
return ans;
}
void CDQ(int l, int r){
if (l >= r) return;
int mid = (l + r) >> 1;
for (int i = l; i <= mid; ++i)
g[i] = f[i], g[i].vis = 0;
for (int i = mid+1; i <= r; ++i)
g[i] = f[i], g[i].vis = 1;
sort(g+l,g+r+1,cmp);
for (int i = l; i <= r; ++i)
if (g[i].vis == 0 && g[i].qid == -1) add(g[i].y,g[i].c); else
if (g[i].vis == 1 && g[i].qid != -1) ans[g[i].qid] += g[i].c*sum(g[i].y);
for (int i = l; i <= r; ++i)
if (g[i].vis == 0 && g[i].qid == -1) add(g[i].y,-g[i].c);
CDQ(l,mid); CDQ(mid + 1, r);
}
int main(){
scanf("%d%d",&s,&m);
int op,x,y,xx,yy,z;
while(scanf("%d",&op)){
if (op == 3) break;
if (op == 1){
scanf("%d%d%d",&x,&y,&z);
f[++tim] = (node){tim,-1,x,y,z};
} else{
scanf("%d%d%d%d",&x,&y,&xx,&yy);
f[++tim] = (node){tim,qtim,xx,yy,1};
f[++tim] = (node){tim,qtim,x-1,yy,-1};
f[++tim] = (node){tim,qtim,xx,y-1,-1};
f[++tim] = (node){tim,qtim++,x-1,y-1,1};
}
}
CDQ(1,tim);
for (int i = 0; i < qtim; ++i)
printf("%d\n",ans[i]);
return 0;
}
/*
0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
*/
总结一下:
自我认为,第二种方法,是一个通用的方法,
然后第一种方法,分治的数组的值,一定要不重复,而且对应区间.