Description 你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作:
命令
参数限制
内容
1 x y A
1<=x,y<=N,A是正整数
将格子x,y里的数字加上A
2 x1 y1 x2 y2
1<=x1<= x2<=N
1<=y1<= y2<=N
输出x1 y1 x2 y2这个矩形内的数字和
3
无
终止程序
HINT1<=N<=500000,操作数不超过200000个,内存限制20M。
对于100%的数据,操作1中的A不超过2000。
首先考虑的是二维数据结构,树状数组或线段树。但是空间复杂度O(n^2),无法承受。
采用CDQ分治。
把所有询问拆分成四个,然后按x为主要关键字、操作顺序为次要关键字排序。
对于一段正在处理的区间(l..r),需要处在这个下标范围内的操作序号恰好也为l..r(这一点如何保证在之后说)。
这样在x有序的情况下,计算所有操作序号为 【注意,是操作序号,不是目前所在的数组下标。】 l..mid的修改对mid+1..r的询问的影响在时间和空间上都可以线性完成。从左往右扫描,按y维护树状数组即可。
所以数组虽然是按x排序的,但并不影响按照操作序号来计算影响。
处理完了之后,还要进行分治。
之前说过要保证处理的区间(l..r)内的操作序号也为l..r,【在这里具体就是l..mid存放l..mid,mid+1..r存放mid+1..r】并且还需要保证是按x排序,所以从开头和中间维护两个指针,线性扫描并且分类存放,最后拷贝回来即可。
注意不要用memset,否则会超时。
最后说一点关于CDQ分治的理解。这道题总共有三个维度:时间,x,y。
通常的做法是,按时间线性扫描,然后维护x,y,这样就需要二维数据结构来维护。
而CDQ分治则是按x线性扫描,数据结构里只维护y,这样只需要一维数据结构。那时间怎么办呢?注意到,时间对结果的影响就是,发生在后的修改不会对发生在前的询问产生影响。CDQ分治就是通过按照时间分治来解决,使每一次具体计算时,所有需要考虑的修改都在所有需要考虑的询问之前,也就是所有需要考虑的修改都会对所有需要考虑的询问产生影响,这样就不用考虑时间了。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct com
{
int k,x,y,z,num,p;
bool operator < (const com &c) const
{
if (x<c.x) return 1;
if (x>c.x) return 0;
return p<c.p;
}
}a[800010],aa[800010];
int s[500010],ans[200010],m,n,cnt;
void solve(int l,int r)
{
if (l==r) return;
int i,j,k,p,q,x,y,z,w,mid=(l+r)/2,p1,p2;
for (i=l;i<=r;i++)
{
if (a[i].p<=mid&&a[i].k==0)
{
for (j=a[i].y;j<=n;j+=j&-j)
s[j]+=a[i].z;
}
if (a[i].p>mid&&a[i].k)
{
for (j=a[i].y;j;j-=j&-j)
ans[a[i].num]+=s[j]*a[i].k;
}
}
for (i=l;i<=r;i++)
if (a[i].p<=mid&&a[i].k==0)
for (j=a[i].y;j<=n;j+=j&-j)
s[j]-=a[i].z;
p1=l;
p2=mid+1;
for (i=l;i<=r;i++)
if (a[i].p<=mid)
aa[p1++]=a[i];
else
aa[p2++]=a[i];
for (i=l;i<=r;i++)
a[i]=aa[i];
solve(l,mid);
solve(mid+1,r);
}
int main()
{
int i,j,k,x,y,z,w;
scanf("%d",&n);
while (scanf("%d",&k)&&k!=3)
{
if (k==1)
{
m++;
scanf("%d%d%d",&a[m].x,&a[m].y,&a[m].z);
a[m].k=0;
a[m].p=m;
}
else
{
scanf("%d%d%d%d",&x,&y,&z,&w);
cnt++;
m++;
a[m]=(com){1,x-1,y-1,0,cnt,m};
m++;
a[m]=(com){-1,z,y-1,0,cnt,m};
m++;
a[m]=(com){-1,x-1,w,0,cnt,m};
m++;
a[m]=(com){1,z,w,0,cnt,m};
}
}
sort(a+1,a+m+1);
solve(1,m);
for (i=1;i<=cnt;i++)
printf("%d\n",ans[i]);
}