2683: 简单题
Time Limit: 50 Sec Memory Limit: 128 MBSubmit: 1268 Solved: 502
[Submit][Status][Discuss]
Description
命令 | 参数限制 | 内容 |
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 | 无 | 终止程序 |
Input
Output
Sample Input
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
Sample Output
5
HINT
Source
显然这题不能树套树 原因自己看数据范围
那我们怎么做?
cdq分治
不过我还没有看懂到底是怎么个做法
反正肯定是这样的:
对时间分治,像归并排序一样考虑[l,mid]对[mid+1,r]的影响(问题1:如何处理影响?)
由于我们读入的时候肯定是x是连续的,所以我们一次可以处理很多询问(问题2:询问如何处理?)
所以我们最后只用维护一下树状数组就可以得到答案(问题3:为什么?)
这里先放上代码 明天我去问一下机房的大神得到这三个问题的答案


/*To The End Of The Galaxy*/ #include<cstdio> #include<cstdlib> #include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<iomanip> #include<stack> #include<map> #include<set> #include<cmath> #include<complex> #define debug(x) cerr<<#x<<"="<<x<<endl #define INF 0x7f7f7f7f #define llINF 0x7fffffffffffll using namespace std; typedef pair<int,int> pii; typedef long long ll; inline int init() {int now=0,ju=1;char c;bool flag=false;while(1){c=getchar();if(c=='-')ju=-1;else if(c>='0'&&c<='9'){now=now*10+c-'0';flag=true;}else if(flag)return now*ju;} } inline long long llinit() {long long now=0,ju=1;char c;bool flag=false;while(1){c=getchar();if(c=='-')ju=-1;else if(c>='0'&&c<='9'){now=now*10+c-'0';flag=true;}else if(flag)return now*ju;} } int n,m; struct node {int t,x,y,d,id,v; }query[2000005],tmpq[2000005]; int ans[2000005],s[2000005]; bool operator < (node a,node b) {if(a.x!=b.x){return a.x<b.x;}else return a.y<b.y; } #define lowbit(x) (x&(-x)) void Add(int now,int v) {for(int i=now;i<=n;i+=lowbit(i)){s[i]+=v;} } int Query(int now) {int sum=0;for(int i=now;i>=1;i-=lowbit(i)){sum+=s[i];}return sum; } #define mid ((l+r)>>1) void solve(int l,int r) {if(l==r)return;solve(l,mid);solve(mid+1,r);int i=l,j=mid+1,last=0;while(j<=r){while(i<=mid&&query[i].t==2){i++;}while(j<=r&&query[j].t==1){j++;}if(i<=mid&&query[i].x<=query[j].x){Add(query[i].y,query[i].v);last=i;i++;}else if(j<=r)ans[query[j].id]+=Query(query[j].y),j++;}for(int i=l;i<=last;i++){if(query[i].t==1){Add(query[i].y,-query[i].v);}}merge(query+l,query+1+mid,query+1+mid,query+1+r,tmpq+l);for(int i=l;i<=r;i++){query[i]=tmpq[i];}return; } bool cmpid(node a,node b) {return a.id<b.id; } int c[2000005],tot=0; int main() {n=init();int xl,xr,yl,yr,v,type;while(type=init(),type!=3){if(type==1){xl=init();yl=init();v=init();++m;query[m].x=xl;query[m].y=yl;query[m].v=v;query[m].t=type;query[m].id=m;}else if(type==2){c[++tot]=m;xl=init();yl=init();xr=init();yr=init();++m;query[m].id=m;query[m].x=xr;query[m].y=yr;query[m].t=type;++m;query[m].id=m;query[m].x=xl-1;query[m].y=yl-1;query[m].t=type;++m;query[m].id=m;query[m].x=xl-1;query[m].y=yr;query[m].t=type;++m;query[m].id=m;query[m].x=xr;query[m].y=yl-1;query[m].t=type;}}int p;solve(1,m);for(int i=1;i<=tot;i++){printf("%d\n",ans[c[i]+1]+ans[c[i]+2]-ans[c[i]+3]-ans[c[i]+4]);}return 0; } /* srO xudyh davidlee1999WTK linkct1999 Orz compiler TDM-GCC 5.9.2 */
UPD:
cdq分治是这样一种东西
首先我们将x1,x2,y1,y2的询问拆分成
[1,x1-1] 贡献为1
[1,y1-1] 贡献为1
[1,x2]贡献为-1
[1,y1-1]贡献为-1
[1,x1-1]贡献为-1
[1,y2]贡献为-1
……
画个图直观显示一下 就是这样
:
我们将一个查询分成了四个 然后是用树状数组维护
然后对时间分治,由于分治的思想我们自然能保证所有的[l,mid]的询问都已经处理过了,且所有[mid+1,r]的修改都已经处理过了
现在我们考虑如何合并
首先我们最后要将这些东西按照x轴归并 这是显然的 不然我们无法保证顺序
那么我们实际上维护了三维偏序集
所以说,我们这样做
我们像归并排序那样维护twopointer,由于我们[l,mid]中的修改,x必然是连续的,[mid+1,r]中的修改,x也必然是连续的
我们就只剩下y轴要考虑了,我们对y轴维护前缀和,具体来说我们用树状数组/线段树维护
由于我们这个时候已经是既按照时间顺序又按照x轴顺序了,那么我们从l到mid的过程中,我们只要看一下右边的x是否还有贡献
大概就是这样……我也不是特别懂