POJ1195 MOBILE/ BZOJ 1176
这两个题题意完全相同,放在一起说吧
题意:
命令 | 参数限制 | 内容 |
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 | 无 | 终止程序 |
标准的三维偏序问题,三个维度分别为x,y和时间t。
在POJ1195中,N<=1024,可以用二维树状数组来做,非常简单。
但在BZOJ1176中,N<=500000,空间开不下,所以用CDQ分治+一维树状数组来做。
对于所有的查询操作,我们拆分成4个。分别查询(0,0)到(x1-1,y1-1),(x2,y2),(x2,y1-1),(x1-1,y2)四个矩形的数字和
然后用容斥原理加减一下就可以算出(x1,y1),(x2,y2)的矩形数字和了。
CDQ分治中,首先按x来排序,对t这个维度进行分治。
那么t<=mid的更新操作都能影响到t'>=mid+1的查询结果。
所以在CDQ(l,r)中,按照x从小到大扫一遍所有操作,遇到更新操作就在树状数组上插入y值,
遇到查询操作就给该操作的结果加上树状数组上查询到的sum(1,y)的值。
接下来用类似归并排序的方法,把t<=mid的操作都放到数组左半部分,t>=mid+1的操作都放到右半部分
这么做之后两半部分的x依然是有序的,就可以递归处理两半部分之间的更新操作对查询操作产生的影响了。
下面是代码,按照POJ1195的输入格式写的
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<string>
#define ll long long
using namespace std;
int n;
int tree[1029];
int cnt=0;
int anscnt=0;
int ans[60111];
struct operation
{
int x,y;
int k;
int id,ansid;
bool type;//0,add;1,query
operation(int x_,int y_,int k_,bool type_,int id_):
x(x_),y(y_),k(k_),type(type_),id(id_){}
operation(int x_,int y_,int k_,int ansid_,bool type_,int id_):
x(x_),y(y_),k(k_),ansid(ansid_),type(type_),id(id_){}
operation(){}
bool operator <(const operation& rhs)const
{
if(x==rhs.x&&y==rhs.y)return type<rhs.type;
if(x==rhs.x)return y<rhs.y;
return x<rhs.x;
}
}op[241111],temp[241111];
void add(int x,int k)
{
for(x;x<=n;x+=x&-x)
tree[x]+=k;
}
int query(int x)
{
int res=0;
for(x;x>0;x-=x&-x)
res+=tree[x];
return res;
}
void cdq(int l,int r)
{
//cout<<l<<" "<<r<<endl;
if(l==r)return;
int mid=l+r>>1,lp=l,rp=mid+1;
for(int i=l;i<=r;++i)
{
if(op[i].id<=mid&&!op[i].type)
add(op[i].y,op[i].k);
if(op[i].id>mid&&op[i].type)
ans[op[i].ansid]+=query(op[i].y)*op[i].k;
}
for(int i=l;i<=r;++i)
if(op[i].id<=mid&&!op[i].type)
add(op[i].y,-op[i].k);
for(int i=l;i<=r;++i)
if(op[i].id<=mid)
temp[lp++]=op[i];
else temp[rp++]=op[i];
for(int i=l;i<=r;++i)
op[i]=temp[i];
cdq(l,mid);cdq(mid+1,r);
}
int main()
{
int o;
while(scanf("%d",&o),o!=3)
{
int x,y,k,x2,y2;
if(o==0)scanf("%d",&n);
if(o==1)
{
scanf("%d%d%d",&x,&y,&k);
++cnt;op[cnt]=operation(x+1,y+1,k,0,cnt);
}else if(o==2){
scanf("%d%d%d%d",&x,&y,&x2,&y2);
anscnt++;
++cnt;op[cnt]=operation(x2+1,y2+1,1,anscnt,1,cnt);
++cnt;op[cnt]=operation(x,y2+1,-1,anscnt,1,cnt);
++cnt;op[cnt]=operation(x2+1,y,-1,anscnt,1,cnt);
++cnt;op[cnt]=operation(x,y,1,anscnt,1,cnt);
}
}
sort(op+1,op+cnt+1);
cdq(1,cnt);
for(int i=1;i<=anscnt;++i)
printf("%d\n",ans[i]);
return 0;
}