「BZOJ2683」简单题 题解

/*
1.若N比较小,则可以用二维的树状数组或线段树来做,但是500000,空间开不下,于是考虑离线CDQ。
子矩阵的数字和表示为也就是二维前缀和,因此一个要查询的子矩阵,
对其有影响的矩阵为s[x2][y2],s[x2][y1-1],s[x1-1][y2],s[x1-1][y1-1]这四个前缀子矩阵
所以,在没有修改操作的时候,这是一个二维偏序了裸题
但当加入了操作顺序这个玩意的时候,就是一个三维偏序。
其实对于操作顺序的处理也就是 CDQ 分治的过程,而每次分治,处理的都仅限于左边的修改对右边查询的影响。
为了判断是查询和修改,我用了一个 opt 变量。

2.注意这个tim的妙用
通过和 vis[] 数组结合,让不属于当前递归的BIT中的贡献被忽略
具体的忽略方式主要是在 ask() 里面
为了和 ask() 配合, add() 函数做出改变,通过另一种方式清空并修改。

3.有一个妙点是对于 opt 的利用,通过这个,处理了将操作和询问一同放入 p[] 的分辨问题
可以直接通过 while 盘掉,注意 while 边界。

4.啊啊啊啊啊啊啊,易错点,
不要把 sort(p+l,p+mid+1) 写成 sort(p+1,p+mid+1)
调了我一个小时多。。。(彩笔一个)

5.双倍经验:[BZOJ1176]Mokia
*/ #include<bits/stdc++.h> using namespace std; const int N = 800005; const int M = 2000005; int n,totp,totq,tim; int opt,x,y,xx,yy,val; int vis[M],ans[N],pos[N]; struct Point{ int x,y,ord,opt,val; bool operator <(const Point &b)const{ return x==b.x?ord<b.ord:x<b.x; } }p[M]; struct BIT{ int c[M]; int lowbit(int x){return -x&x;} void add(int x,int val){ for(;x<=n;x+=lowbit(x)){ if(vis[x]!=tim)vis[x]=tim,c[x]=val; else c[x]+=val; } } int ask(int x){ int res=0; for(;x;x-=lowbit(x)) if(vis[x]==tim)res+=c[x]; return res; } }bit; void solve(int l,int r){ if(l==r)return ; int mid=(l+r)>>1; solve(l,mid);solve(mid+1,r); sort(p+l,p+mid+1);sort(p+mid+1,p+r+1); tim++; int p1=l,p2=mid+1; while(p2<=r){ while(p1<=mid&&p[p1].opt==2)p1++; while(p2<=r&&p[p2].opt==1)p2++; if(p1<=mid&&p[p1].x<=p[p2].x)bit.add(p[p1].y,p[p1].val),p1++; else if(p2<=r)ans[p[p2].ord]+=bit.ask(p[p2].y),p2++; } } int main(){ scanf("%d",&n); while(1){ scanf("%d",&opt); if(opt==3)break; scanf("%d%d",&x,&y); if(opt==1){ scanf("%d",&val); p[++totp]={x,y,totp,opt,val}; }else{ scanf("%d%d",&xx,&yy); pos[++totq]=totp; p[++totp]={x-1,y-1,totp,opt,0}; p[++totp]={xx,y-1,totp,opt,0}; p[++totp]={x-1,yy,totp,opt,0}; p[++totp]={xx,yy,totp,opt,0}; } } solve(1,totp); for(int i=1;i<=totq;i++) printf("%d\n",ans[pos[i]+1]-ans[pos[i]+2]-ans[pos[i]+3]+ans[pos[i]+4]); return 0; }

 

posted @ 2021-03-15 20:04  _Famiglistimo  阅读(78)  评论(0编辑  收藏  举报