cdq分治学习笔记


1.作用

可以用来搞一些离线的题目,用于代替复杂的数据结构入树套树。

2.做法

1.分治左边区间。

2.计算左边区间对右边区间答案的贡献。

3.分治右边区间。

4.将当前区间排序。

5.return。

3.例题

1.树状数组

单点修改区间查询。

将所有操作记录cdq分治。

每次分治时递归完了如果在左区间并且是添加操作就反手将sum+=jia,如果是询问就把ans[]+=sum。

莫得代码。

2.经典三维偏序。

【模板】三维偏序(陌上花开)

设三维分别为a,b,c。

1.第一维排好序后分治,第二位用归并排序保证在这个地方询问时前面的b比当前的b小,然后第三位用树状数组压入,查询c比当前晓得。

2.只有在a在左区间时才执行添加操作,a在右区间时执行询问操作,这样就保证了询问的a永远大于加入的a。

3.总结就是第一维靠分治保证小于,第二维靠归并排序,第三位靠树状数组。

4.递归结束时要清空树状数组。

代码如下:

#include<bits/stdc++.h>
using namespace std; int n,k,shu[200005],dui[100005],cnt,daan[100005],ans[100005]; struct pigu { int xx,yy,zz,id; }a[100005]; int cmp1(pigu x,pigu y) { if(x.xx!=y.xx) return x.xx<y.xx; if(x.yy!=y.yy) return x.yy<y.yy; if(x.zz!=y.zz) return x.zz<y.zz; } int cmp2(pigu x,pigu y) { if(x.yy!=y.yy) return x.yy<y.yy; if(x.zz!=y.zz) return x.zz<y.zz; return x.xx<y.xx; } int lowbit(int x) { return x&(-x); } void cdq(int l,int r) { if(l==r) return; int mid=(l+r)>>1; cdq(l,mid); cdq(mid+1,r); sort(a+l,a+r+1,cmp2); for(int i=l;i<=r;i++) if(a[i].xx<=mid) { int now=a[i].zz; while(now<=k) { shu[now]++; now+=lowbit(now); } } else { int now=a[i].zz; while(now) { daan[a[i].id]+=shu[now]; now-=lowbit(now); } } for(int i=l;i<=r;i++) if(a[i].xx<=mid) { int now=a[i].zz; while(now<=k) { shu[now]--; now+=lowbit(now); } } } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d%d%d",&a[i].xx,&a[i].yy,&a[i].zz); a[i].id=i; } sort(a+1,a+n+1,cmp1); for(int i=1;i<=n;) { int j=i+1; while(a[i].xx==a[j].xx&&a[i].yy==a[j].yy&&a[i].zz==a[j].zz) j++; ++cnt; while(i<j) { dui[a[i].id]=a[j-1].id; i++; } } for(int i=1;i<=n;i++) a[i].xx=i; cdq(1,n); for(int i=1;i<=n;i++) { ans[daan[dui[a[i].id]]]++; } for(int i=0;i<n;i++) printf("%d\n",ans[i]); return 0; } /* 3 3 1 2 3 0 1 2 1 2 3 */
View Code

3.P2163 [SHOI2007]园丁的烦恼

给出了一些点,一堆询问求一个矩形内有几个点。

一开始感觉是三维偏序,要两个log,结果发现时间没用,就成二维偏序了。

cdq分治,二维偏序,将询问拆成四个(跟矩阵前缀和差不多)就可以了。

排序时先考虑y再考虑是询问还是添加。

代码如下:

#include<bits/stdc++.h>
using namespace std; const int N=5000005; int n,m,cnt,ma,ans[N]; struct pigu { int pan,x,y,zai,xp; friend bool operator <(pigu x,pigu y) { return x.y<=y.y; } }a[N],hu[N]; inline bool cmp(pigu x,pigu y) { return x.x==y.x?(x.y==y.y?x.pan<y.pan:x.y<y.y):x.x<y.x; } inline int read() { char c=getchar(); int x=0,f=1; while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();} while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();} return x*f; } inline void solve(int l,int r) { if(l==r) return; int mid=(l+r)>>1; solve(l,mid);solve(mid+1,r); int o=l,ll=l,mm=mid+1,daan=0; while(ll<=mid&&mm<=r) { if(a[ll]<a[mm]) { if(a[ll].pan==1) daan++; hu[o++]=a[ll++]; } else { if(a[mm].pan==2) ans[a[mm].zai]+=a[mm].xp*daan; hu[o++]=a[mm++]; } } while(ll<=mid) hu[o++]=a[ll++]; while(mm<=r) { if(a[mm].pan==2) ans[a[mm].zai]+=a[mm].xp*daan; hu[o++]=a[mm++]; } for(int i=l;i<=r;i++) a[i]=hu[i]; } int main() { n=read();m=read(); for(int i=1;i<=n;i++) { a[++cnt].x=read()+1;a[cnt].y=read()+1;ma=max(ma,a[cnt].y); a[cnt].pan=1; } for(int i=1,x,y,xx,yy;i<=m;i++) { x=read()+1;y=read()+1;xx=read()+1;yy=read()+1; a[++cnt].pan=2;a[cnt].x=x-1,a[cnt].y=y-1;a[cnt].zai=i;a[cnt].xp=1; a[++cnt].pan=2;a[cnt].x=x-1;a[cnt].y=yy;a[cnt].zai=i;a[cnt].xp=-1; a[++cnt].pan=2;a[cnt].x=xx;a[cnt].y=y-1;a[cnt].zai=i;a[cnt].xp=-1; a[++cnt].pan=2;a[cnt].x=xx;a[cnt].y=yy;a[cnt].zai=i;a[cnt].xp=1; } sort(a+1,a+cnt+1,cmp); solve(1,cnt); for(int i=1;i<=m;i++) cout<<ans[i]<<"\n"; } 
View Code

4.P4390 [BOI2007]Mokia 摩基亚

就是上一个题增加了一手修改,就再加上时间,就成了三维偏序,每个询问还是拆成四个:查询时间小于此询问,x<X,y<Y的修改。

代码如下:

#include<bits/stdc++.h>
using namespace std; const int N=2000005; int n,m,cnt,ma,ans[N],s[N]; struct pigu { int pan,x,y,zai,xp,jia; friend bool operator <(pigu x,pigu y) { return x.x==y.x?(x.y==y.y?x.pan<y.pan:x.y<y.y):x.x<y.x; } }a[N],hu[N]; inline bool cmp(pigu x,pigu y) { return x.x==y.x?(x.y==y.y?x.pan<y.pan:x.y<y.y):x.x<y.x; } inline int lowbit(int x) { return x&(-x); } inline void add(int x,int ad) { for(int i=x;i<=ma;i+=lowbit(i)) s[i]+=ad; } inline int query(int x) { int daan=0; while(x) { daan+=s[x]; x-=lowbit(x); } return daan; } inline int read() { char c=getchar(); int x=0,f=1; while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();} while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();} return x*f; } inline void solve(int l,int r) { if(l==r) return; int mid=(l+r)>>1; solve(l,mid);solve(mid+1,r); int o=l,ll=l,mm=mid+1; while(ll<=mid&&mm<=r) { if(a[ll]<a[mm]) { if(a[ll].pan==1) add(a[ll].y,a[ll].jia); hu[o++]=a[ll++]; } else { if(a[mm].pan==2) ans[a[mm].zai]+=a[mm].xp*query(a[mm].y); hu[o++]=a[mm++]; } } while(ll<=mid) add(a[ll].y,a[ll].jia),hu[o++]=a[ll++]; while(mm<=r) { if(a[mm].pan==2) ans[a[mm].zai]+=a[mm].xp*query(a[mm].y); hu[o++]=a[mm++]; } for(int i=l;i<=mid;i++) if(a[i].pan==1) add(a[i].y,-a[i].jia); for(int i=l;i<=r;i++) a[i]=hu[i]; } int main() { int xipan=read(),gu=0; while(xipan!=3) { int x,y,xx,yy; if(xipan==0) ma=read()+1; if(xipan==1) { a[++cnt].x=read()+1;a[cnt].pan=1;a[cnt].y=read()+1;a[cnt].jia=read(); } if(xipan==2) { x=read()+1;y=read()+1;xx=read()+1;yy=read()+1; a[++cnt].pan=2;a[cnt].x=x-1,a[cnt].y=y-1;a[cnt].zai=++gu;a[cnt].xp=1; a[++cnt].pan=2;a[cnt].x=x-1;a[cnt].y=yy;a[cnt].zai=gu;a[cnt].xp=-1; a[++cnt].pan=2;a[cnt].x=xx;a[cnt].y=y-1;a[cnt].zai=gu;a[cnt].xp=-1; a[++cnt].pan=2;a[cnt].x=xx;a[cnt].y=yy;a[cnt].zai=gu;a[cnt].xp=1; } if(xipan==3) break; xipan=read(); } solve(1,cnt); for(int i=1;i<=gu;i++) cout<<ans[i]<<"\n"; }
View Code

 

5.P4027 [NOI2007]货币兑换

首先斜率优化那个样子搞一哈虱子,然后发现x不递增,斜率不递增,就可以用splay动态维护凸包,二分查找最近的斜率。

还可以cdq分治一波,先总体全部按照k排序,左区间搞一手斜率单调递减,右区间由于k拍好了序,就可以直接在左边维护好的东西头查询。

注意每次操作前要先把整个区间按照时间排序拍好,递归完了就可以不需要管时间了。

代码如下:

#include<bits/stdc++.h>
using namespace std; const int N=1e5+5; const double eps=1e-7; const double inf=1e10; int n; double f[N]; struct pigu { double x,y,a,b,r,k; int id; }wen[N],hu[N],hu2[N]; inline int read() { char c=getchar(); int x=0,f=1; while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();} while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();} return x*f; } inline bool cmp(pigu x,pigu y) { return x.k<y.k; } inline double xl(int x,int y) { if(fabs(wen[x].x-wen[y].x)<=eps) return inf; return (wen[x].y-wen[y].y)/(wen[x].x-wen[y].x); } int st[N]; inline void merge(int l,int r) { int mid=(l+r)>>1; int t1=l,t2=mid+1; for(int i=l;i<=r;++i) if(t1<=mid&&(t2>r||wen[t1].x<wen[t2].x+eps)) hu[i]=wen[t1++]; else hu[i]=wen[t2++]; for(int i=l;i<=r;++i) wen[i]=hu[i]; } inline void solve(int l,int r) { if(l==r) { f[l]=max(f[l],f[l-1]); wen[l].y=f[l]/(wen[l].a*wen[l].r+wen[l].b); wen[l].x=wen[l].y*wen[l].r; return; } int mid=(l+r)>>1,ll=l-1,mm=mid; for(int i=l;i<=r;i++) { if(wen[i].id<=mid) hu[++ll]=wen[i]; else hu[++mm]=wen[i]; } for(int i=l;i<=r;i++) wen[i]=hu[i]; solve(l,mid); int top=0; for(int i=l;i<=mid;i++) { while(top>=2&&xl(st[top],st[top-1])<xl(i,st[top])+eps) top--; st[++top]=i; } for(int i=mid+1;i<=r;i++) { while(top>1&&xl(st[top],st[top-1])<wen[i].k+eps) top--; f[wen[i].id]=max(f[wen[i].id],wen[i].a*wen[st[top]].x+wen[i].b*wen[st[top]].y); } solve(mid+1,r);merge(l,r); } int main() { n=read();scanf("%lf",&f[0]); for(int i=1;i<=n;i++) { scanf("%lf%lf%lf",&wen[i].a,&wen[i].b,&wen[i].r); wen[i].k=-wen[i].a/wen[i].b;wen[i].id=i; } sort(wen+1,wen+n+1,cmp); solve(1,n); printf("%.3lf",f[n]); }
View Code

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM