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