多方查找找到了2008年陳丹琪引入CDQ分治的 從《Cash》談一類分治算法的應用.doc ,CDQ分治的名字由來也是她.
什麼叫CDQ分治呢?來看一道二維數點題p1357.
看了一眼題,我會樹狀數組!
現在拿它來引入CDQ分治.先全部按照x排序,由於區間[mid+1,r]不會再對區間[l,mid]有貢獻,對於區間[l,r]內的貢獻都可以分三步進行.
1.算區間[l,mid]內部相互的貢獻
2.算區間[mid+1,r]內部相互的貢獻
3.算[l,mid]對於[mid+1,r]的貢獻
現在考慮如何更快的計算第3步?假設1,2步後,兩個小區間內部變成了按y排序的,我們像歸併一樣的把兩個小區間按y合併成一個大區間並得到[l,mid]對於[mid+1,r]的貢獻.
先放一個nlog^2n的算法
bool Orz(node a,node b) { return a.x<b.x; } void CDQ(int l,int r) { if(l==r) return ;//自己對自己當然沒貢獻了 int mid=(l+r)/2; CDQ(l,mid); CDQ(mid+1,r); //簡陋的樹狀數組實現貢獻統計 for(int i=l;i<=mid;i++)//怎麼還用樹狀數組啊? add(o[i].y,1); for(int i=mid+1;i<=r;i++) ans[o[i].i]+=ask(o[i].y);//算貢獻 for(int i=l;i<=mid;i++)//怎麼又減回去了啊? add(o[i].y,-1); sort(o+l,o+r+1);//簡陋的sort排序 } int main() { freopen("123.in","r",stdin); n=read(); for(i=1;i<=n;i++) { o[i].x=read(); o[i].y=read(); o[i].i=i;//記錄原始位置 } sort(o+1,o+1+n,Orz); CDQ(1,n);//調用分治 for(i=1;i<=n;i++) cout<<ans[i]<<endl; }
再放一個好看的
struct node { int x,y; int i; }o[60010],temp[60010]; int ans[60010]; int i,n; inline bool Orz(node a,node b) { return a.x==b.x?a.y<b.y:a.x<b.x; } inline void CDQ(int l,int r) { if(l==r) return ;//自己對自己當然沒貢獻了 int mid=(l+r)/2; CDQ(l,mid); CDQ(mid+1,r); int a=l,b=mid+1,tot=l; for(;a<=mid||b<=r;tot++)//合併區間並統計答案 { if(a<=mid&&o[a].y<=o[b].y||b>r) { temp[tot]=o[a]; a++; } else { ans[o[b].i]+=a-l;//統計答案 temp[tot]=o[b]; b++; } } for(int i=l;i<=r;i++) o[i]=temp[i]; } int main() { n=read(); for(i=1;i<=n;i++) { o[i].x=read(); o[i].y=read(); o[i].i=i;//記錄原始位置 } sort(o+1,o+1+n,Orz); CDQ(1,n);//調用分治 for(i=1;i<=n;i++) { write(ans[i]); putchar(10); } }
用CDQ A掉後可以去p2026交一下.可以看到兩道題幾乎一樣,區別在於aibi的範圍.但是離散化也能寫啊QAQ
然後來看一個三位數點問題.
首先題目有些不一樣了,假設隨從i可以完爆f(i)個隨從,本題詢問的是f()=i的數量.
然後來做題.
爲了繼續套用CDQ的模板,我們可先按照x,y,z爲第一,二,三關鍵字從小到大進行排序,這樣可以保證任意區間[l,r]內的右區間不會對左區間產生貢獻.爲了O(r-l)的決策貢獻,我們可以使得CDQ()兩個子區間後,每個區間內部都是以y爲第一關鍵字進行排序,然後繼續按照歸併排序的方法合併兩個區間並計算貢獻,具體計算方法也不一樣了,並且y的單調性並不能保證z的單調性,我們需要樹狀數組了.
int i; int n,c[200010],ans[100010],cnt[100010]; struct node { int x,y,z; int i; }o[100010],temp[100010]; inline int lowbit(int x) { return x&(-x); } inline void add(int x,int k) { while(x<=200000) { c[x]+=k; x+=lowbit(x); } } inline int ask(int x) { int sum=0; while(x) { sum+=c[x]; x-=lowbit(x); } return sum; } inline bool Orz(node a,node b) { return a.x==b.x?(a.y==b.y?(a.z==b.z?a.i<b.i:a.z<b.z):a.y<b.y):a.x<b.x; } void CDQ(int l,int r) { if(l==r) return ; int mid=(l+r)/2; CDQ(l,mid); CDQ(mid+1,r); int a=l,b=mid+1,tot=l; for(;a<=mid||b<=r;tot++) { if(a<=mid&&(o[a].y<o[b].y||(o[a].y==o[b].y&&o[a].z<o[b].z)||(o[a].y==o[b].y&&o[a].z==o[b].z&&o[a].x<o[b].x))||b>r) { temp[tot]=o[a]; add(o[a].z,1); a++; } else { ans[o[b].i]+=ask(o[b].z); temp[tot]=o[b]; b++; } } while(a>l) { a--; add(o[a].z,-1); } for(tot=l;tot<=r;tot++) o[tot]=temp[tot]; } int main() { n=read();i=read(); for(i=1;i<=n;i++) { o[i].x=read(); o[i].y=read(); o[i].z=read(); o[i].i=i; } sort(o+1,o+1+n,Orz); CDQ(1,n); for(i=1;i<=n;i++) cnt[ans[i]]++; for(i=0;i<n;i++) { write(cnt[i]); putchar(10); } }
這個代碼在洛谷上仍然過不了,在本校oj上倒是跑的很好.這說明我的代碼還是很糙...//2019 2 20 14:37
不管了我們繼續.
又有了一道CDQ.看一眼題目,是帶修改的離線二維數點,如何來做呢?
先考慮如何讀入.
當然是仁者見仁智者見智了.反正修改和詢問是要平等的放在同一個數組(結構體)裏的.我的方法是存在結構體裏,並把詢問拆成四個(假設x2>=x1,y2>=y1)sum(x2,y2)+sum(x1-1,y1-1)-sum(x2,y1-1)-sum(x1-1,y2).存一個i表示時間戳.並把詢問的flag設爲1,修改的flag設爲0.
然後可以發現,一個區間查詢sum(x,y)等價於 "查詢所有時間戳小於它且x小於等於它且y小於等於它的flag=0的點的權和".這個和三維偏序好像啊,CDQ+樹狀數組A掉.
int i,T,tx1,tx2,ty1,ty2; int n,tot,c[500010]; struct node { int x,y; int i,sum; int flag; }o[800010],temp[800010];//操作數*4 int lowbit(int x) { return x&(-x); } inline void add(int x,int k) { while(x<=n) { c[x]+=k; x+=lowbit(x); } } inline int ask(int x) { int sum=0; while(x) { sum+=c[x]; x-=lowbit(x); } return sum; } void CDQ(int l,int r) { if(l==r) return ; int mid=(l+r)/2,t=l,a=l,b=mid+1; CDQ(l,mid); CDQ(mid+1,r); for(;a<=mid||b<=r;t++) { if(a<=mid&&o[a].x<=o[b].x||b>r)//如果a可以用且a的x小於等於b的x或b不能用 { temp[t]=o[a]; if(!o[a].flag) add(o[a].y,o[a].sum); a++; } //else if(b<=r&&o[a].x>o[b].x||a>mid) else//如果b的x小於a的x或a不能用 { if(o[b].flag) o[b].sum+=ask(o[b].y); temp[t]=o[b]; b++; } } //從上面的if可以發現y的大小不會影響答案 for(a=l;a<=mid;a++) if(!o[a].flag) add(o[a].y,-o[a].sum); for(t=l;t<=r;t++) o[t]=temp[t]; return ; } inline bool Orz(node a,node b) { return a.i<b.i; } int main() { n=read(); for(T=read();T!=3;T=read()) { tot++; if(T==1) { o[tot].x=read(); o[tot].y=read(); o[tot].sum=read(); o[tot].i=tot; //o[tot].flag=0; } else { tx1=read();ty1=read();tx2=read();ty2=read(); //下面的min,max是爲了防止不合法數據.. o[tot].x=min(tx1,tx2)-1; o[tot].y=min(ty1,ty2)-1; o[tot].i=tot; o[tot].flag=1; tot++; o[tot].x=max(tx1,tx2); o[tot].y=max(ty1,ty2); o[tot].i=tot; o[tot].flag=1; tot++; o[tot].x=max(tx1,tx2); o[tot].y=min(ty1,ty2)-1; o[tot].i=tot; o[tot].flag=1; tot++; o[tot].x=min(tx1,tx2)-1; o[tot].y=max(ty1,ty2); o[tot].i=tot; o[tot].flag=1; } } //這道"三維偏序"不需要事先sort了,因爲有一維i有序了 CDQ(1,tot); //這裏的o一定是以x爲第一關鍵字排列的,第二關鍵字不太清楚也不太重要 sort(o+1,o+1+tot,Orz);//恢復原排列 for(i=1;i<=tot;) if(o[i].flag) { write(o[i].sum+o[i+1].sum-o[i+2].sum-o[i+3].sum);// putchar(10); i+=4; } else i++; return 0; }
繼續繼續.
看到題目,逆序對?我會樹狀數組!
動態的?n^2logn!
我瞎說的.
讀入的時候進行一番操作,可以繼續記錄每個點的權值爲x,位置爲i,被刪除的時間戳t(最後也沒被刪去的設爲n+1好了).那麼可以算每個點的答案爲.x1>x&&i1<i&&t1<=t和x1<x&&i1>i&&t1<=t的數量.可以發現t1=t的那部分重複了.我們需要一個簡單容斥.這樣一處理就又是三維偏序了?我們可以自豪的說出:我會CDQ!
具體來說,大家可以先寫個n^2的暴力,過樣例後再調用CDQ寫個暴力,再用CDQ寫個更好的算法.
我也會用一晚上的時間搞出多個版本放在下面.
int i,tx; int n,m,c[100010]; inline int lowbit(int x) { return x&(-x); } inline void add(int x,int k) { while(x<=n) { c[x]+=k; x+=lowbit(x); } } inline int ask(int x) { int sum=0; while(x) { sum+=c[x]; x-=lowbit(x); } return sum; } struct node { int x,i,t;//權值,位置,時間 int ans; }o[100010],temp[100010]; bool x_(node a,node b) { return a.x<b.x; } bool t_(node a,node b) { return a.t<b.t; } int main() { freopen("123.in","r",stdin); n=read();m=read(); for(i=1;i<=n;i++) { o[i].x=read(); o[i].i=i; o[i].t=m+1; } sort(o+1,o+1+n,x_); for(i=1;i<=m;i++) { tx=read(); o[tx].t=i; } sort(o+1,o+1+n,t_); for(int a=1;a<=m;a++) { for(int b=a+1;b<=n;b++) { if(o[a].x>o[b].x&&o[a].i<o[b].i||o[a].x<o[b].x&&o[a].i>o[b].i) { o[a].ans++; } } //cout<<o[a].ans<<endl; } for(int a=m+1;a<=n;a++) { for(int b=a+1;b<=n;b++) { if(o[a].x>o[b].x&&o[a].i<o[b].i||o[a].x<o[b].x&&o[a].i>o[b].i) { o[m+1].ans++; } } } for(i=m;i;i--) { o[i].ans+=o[i+1].ans; } for(i=1;i<=m;i++) { cout<<o[i].ans<<endl; } }
int i,tx; int n,m,c[100010]; inline int lowbit(int x) { return x&(-x); } inline void add(int x,int k) { while(x<=n) { c[x]+=k; x+=lowbit(x); } } inline int ask(int x) { int sum=0; while(x) { sum+=c[x]; x-=lowbit(x); } return sum; } struct node { int x,i,t;//權值,位置,時間 int ans; }o[100010],temp[100010]; int sum; bool x_(node a,node b) { return a.x<b.x; } bool t_(node a,node b) { return a.t<b.t; } void three() { for(int a=1;a<=m;a++) for(int b=a+1;b<=n;b++) if(o[a].x>o[b].x&&o[a].i<o[b].i) o[a].ans++; for(int a=1;a<=m;a++) for(int b=a+1;b<=n;b++) if(o[a].x<o[b].x&&o[a].i>o[b].i) o[a].ans++; } void two(int l,int r) { sort(o+1,o+1+n,t_); sort(o+l,o+r+1,x_); for(int a=l;a<=r;a++) { sum+=a-l-ask(o[a].i); add(o[a].i,1); } memset(c,0,sizeof(c)); } int main() { // freopen("123.in","r",stdin); n=read();m=read(); for(i=1;i<=n;i++) { o[i].x=read(); o[i].i=i; o[i].t=m+1; } sort(o+1,o+1+n,x_); for(i=1;i<=m;i++) { tx=read(); o[tx].t=i; } two(m+1,n); sort(o+1,o+1+n,t_); three(); sort(o+1,o+1+n,t_); o[m+1].ans=sum; for(i=m;i;i--) o[i].ans+=o[i+1].ans; for(i=1;i<=m;i++) cout<<o[i].ans<<endl; }
int sum; bool x_(node a,node b) { return a.x<b.x; } bool _x(node a,node b) { return a.x>b.x; } bool t_(node a,node b) { return a.t<b.t; } void CDQ1(int l,int r) { if(l==r) return ; int mid=(l+r)/2,a=l,b=mid+1,tot=l; CDQ1(l,mid); CDQ1(mid+1,r); for(;a<=mid||b<=r;tot++) if(a<=mid&&o[a].x<o[b].x||b>r) { o[a].ans+=b-mid-1-ask(o[a].i); temp[tot]=o[a]; a++; } else { add(o[b].i,1); temp[tot]=o[b]; b++; } for(b=mid+1;b<=r;b++) add(o[b].i,-1); for(tot=l;tot<=r;tot++) o[tot]=temp[tot]; } void CDQ2(int l,int r) { if(l==r) return ; int mid=(l+r)/2,a=l,b=mid+1,tot=l; CDQ2(l,mid); CDQ2(mid+1,r); for(;a<=mid||b<=r;tot++) if(a<=mid&&o[a].x>o[b].x||b>r) { o[a].ans+=ask(o[a].i); temp[tot]=o[a]; a++; } else { add(o[b].i,1); temp[tot]=o[b]; b++; } for(b=mid+1;b<=r;b++) add(o[b].i,-1); for(tot=l;tot<=r;tot++) o[tot]=temp[tot]; } void three() { CDQ1(1,m); /*for(int a=1;a<=m;a++) for(int b=a+1;b<=m;b++) if(o[a].x>o[b].x&&o[a].i<o[b].i) o[a].ans++;*/ sort(o+1,o+1+n,t_); CDQ2(1,m); sort(o+1,o+1+n,t_); for(int a=1;a<=m;a++) for(int b=m+1;b<=n;b++) if(o[a].x>o[b].x&&o[a].i<o[b].i) o[a].ans++; for(int a=1;a<=m;a++) for(int b=m+1;b<=n;b++) if(o[a].x<o[b].x&&o[a].i>o[b].i) o[a].ans++; } void two(int l,int r) { sort(o+1,o+1+n,t_); sort(o+l,o+r+1,x_); for(int a=l;a<=r;a++) { sum+=a-l-ask(o[a].i); add(o[a].i,1); } memset(c,0,sizeof(c)); } int main() { freopen("123.in","r",stdin); n=read();m=read(); for(i=1;i<=n;i++) { o[i].x=read(); o[i].i=i; o[i].t=m+1; } sort(o+1,o+1+n,x_); for(i=1;i<=m;i++) { tx=read(); o[tx].t=i; } two(m+1,n); sort(o+1,o+1+n,t_); three(); sort(o+1,o+1+n,t_); o[m+1].ans=sum; for(i=m;i;i--) o[i].ans+=o[i+1].ans; for(i=1;i<=m;i++) cout<<o[i].ans<<endl; }
#include<iostream> #include<iomanip> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<vector> #include<map> #include<stack> #include<queue> #include<deque> #include<set> using namespace std; char buf[1<<15],*fs,*ft; inline char getc(){ return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:* fs++; } inline int read(){ int This=0,F=1; char ch=getc(); while(ch<'0'||ch>'9'){ if(ch=='-') F=-1; ch=getc(); } while(ch>='0'&&ch<='9'){ This=(This<<1)+(This<<3)+ch-'0'; ch=getc(); } return This*F; } inline void write(int x) { if(x==0) { putchar('0'); return; } if(x<0) { putchar('-'); x=-x; } int num=0;char ch[16]; while(x) ch[++num]=x%10+'0',x/=10; while(num) putchar(ch[num--]); } int i,tx; int n,m,c[100010]; inline int lowbit(int x) { return x&(-x); } inline void add(int x,int k) { while(x<=n) { c[x]+=k; x+=lowbit(x); } } inline int ask(int x) { int sum=0; while(x) { sum+=c[x]; x-=lowbit(x); } return sum; } struct node { int x,i,t;//權值,位置,時間 int ans; }o[100010],temp[100010]; int sum; bool x_(node a,node b) { return a.x<b.x; } bool _x(node a,node b) { return a.x>b.x; } bool t_(node a,node b) { return a.t<b.t; } void CDQ1(int l,int r) { if(l==r) return ; int mid=(l+r)/2,a=l,b=mid+1,tot=l; CDQ1(l,mid); CDQ1(mid+1,r); for(;a<=mid||b<=r;tot++) if(a<=mid&&o[a].x<o[b].x||b>r) { o[a].ans+=b-mid-1-ask(o[a].i); temp[tot]=o[a]; a++; } else { add(o[b].i,1); temp[tot]=o[b]; b++; } for(b=mid+1;b<=r;b++) add(o[b].i,-1); for(tot=l;tot<=r;tot++) o[tot]=temp[tot]; } void CDQ2(int l,int r) { if(l==r) return ; int mid=(l+r)/2,a=l,b=mid+1,tot=l; CDQ2(l,mid); CDQ2(mid+1,r); for(;a<=mid||b<=r;tot++) if(a<=mid&&o[a].x>o[b].x||b>r) { o[a].ans+=ask(o[a].i); temp[tot]=o[a]; a++; } else { add(o[b].i,1); temp[tot]=o[b]; b++; } for(b=mid+1;b<=r;b++) add(o[b].i,-1); for(tot=l;tot<=r;tot++) o[tot]=temp[tot]; } void three() { CDQ1(1,m); /*for(int a=1;a<=m;a++) for(int b=a+1;b<=m;b++) if(o[a].x>o[b].x&&o[a].i<o[b].i) o[a].ans++;*/ sort(o+1,o+1+n,t_); CDQ2(1,m); sort(o+1,o+1+n,t_); sort(o+1,o+1+m,x_); sort(o+1+m,o+n+1,x_); int a=1,b=m+1; for(;a<=m||b<=n;) { if(a<=m&&o[a].x<o[b].x||b>n) { o[a].ans+=b-m-1-ask(o[a].i); a++; } else { add(o[b].i,1); b++; } } memset(c,0,sizeof(c)); sort(o+1,o+1+m,_x); sort(o+1+m,o+1+n,_x);/* sort(o+1,o+1+n,t_); for(int a=1;a<=m;a++) for(int b=m+1;b<=n;b++) if(o[a].x<o[b].x&&o[a].i>o[b].i) o[a].ans++;*/ for(a=1,b=m+1;a<=m||b<=n;) { if(a<=m&&o[a].x>o[b].x||b>n) { o[a].ans+=ask(o[a].i); a++; } else { add(o[b].i,1); b++; } } sort(o+1,o+1+n,t_); } void two(int l,int r) { sort(o+1,o+1+n,t_); sort(o+l,o+r+1,x_); for(int a=l;a<=r;a++) { sum+=a-l-ask(o[a].i); add(o[a].i,1); } memset(c,0,sizeof(c)); } int main() { // freopen("123.in","r",stdin); n=read();m=read(); for(i=1;i<=n;i++) { o[i].x=read(); o[i].i=i; o[i].t=m+1; } sort(o+1,o+1+n,x_); for(i=1;i<=m;i++) { tx=read(); o[tx].t=i; } two(m+1,n); sort(o+1,o+1+n,t_); three(); sort(o+1,o+1+n,t_); o[m+1].ans=sum; for(i=m;i;i--) o[i].ans+=o[i+1].ans; for(i=1;i<=m;i++) { write(o[i].ans); putchar(10); } }
long long i,tx; long long n,m,c[100010]; inline long long lowbit(long long x) { return x&(-x); } inline void add(long long x,long long k) { while(x<=n) { c[x]+=k; x+=lowbit(x); } } inline long long ask(long long x) { long long sum=0; while(x) { sum+=c[x]; x-=lowbit(x); } return sum; } struct node { long long x,i,t;//權值,位置,時間 long long ans; }o[100010],temp[100010]; long long sum; inline bool x_(node a,node b) { return a.x<b.x; } inline bool _x(node a,node b) { return a.x>b.x; } inline bool t_(node a,node b) { return a.t<b.t; } void CDQ1(long long l,long long r) { if(l==r) return ; long long mid=(l+r)/2,a=l,b=mid+1,tot=l; CDQ1(l,mid); CDQ1(mid+1,r); for(;a<=mid||b<=r;tot++) if(a<=mid&&o[a].x<o[b].x||b>r) { o[a].ans+=b-mid-1-ask(o[a].i); temp[tot]=o[a]; a++; } else { add(o[b].i,1); temp[tot]=o[b]; b++; } for(b=mid+1;b<=r;b++) add(o[b].i,-1); for(tot=l;tot<=r;tot++) o[tot]=temp[tot]; } void CDQ2(long long l,long long r) { if(l==r) return ; long long mid=(l+r)/2,a=l,b=mid+1,tot=l; CDQ2(l,mid); CDQ2(mid+1,r); for(;a<=mid||b<=r;tot++) if(a<=mid&&o[a].x>o[b].x||b>r) { o[a].ans+=ask(o[a].i); temp[tot]=o[a]; a++; } else { add(o[b].i,1); temp[tot]=o[b]; b++; } for(b=mid+1;b<=r;b++) add(o[b].i,-1); for(tot=l;tot<=r;tot++) o[tot]=temp[tot]; } void three() { CDQ1(1,m); sort(o+1,o+1+n,t_); CDQ2(1,m); sort(o+1,o+1+n,t_); sort(o+1,o+1+m,x_); sort(o+1+m,o+n+1,x_); long long a=1,b=m+1; for(;a<=m||b<=n;) if(a<=m&&o[a].x<o[b].x||b>n) { o[a].ans+=b-m-1-ask(o[a].i); a++; } else { add(o[b].i,1); b++; } memset(c,0,sizeof(c)); sort(o+1,o+1+m,_x); sort(o+1+m,o+1+n,_x); for(a=1,b=m+1;a<=m||b<=n;) if(a<=m&&o[a].x>o[b].x||b>n) { o[a].ans+=ask(o[a].i); a++; } else { add(o[b].i,1); b++; } sort(o+1,o+1+n,t_); } void two(long long l,long long r) { sort(o+1,o+1+n,t_); sort(o+l,o+r+1,x_); for(long long a=l;a<=r;a++) { sum+=a-l-ask(o[a].i); add(o[a].i,1); } memset(c,0,sizeof(c)); } int main() { n=read();m=read(); for(i=1;i<=n;i++) { o[i].x=read(); o[i].i=i; o[i].t=m+1; } sort(o+1,o+1+n,x_); for(i=1;i<=m;i++) { tx=read(); o[tx].t=i; } two(m+1,n); sort(o+1,o+1+n,t_); three(); sort(o+1,o+1+n,t_); o[m+1].ans=sum; for(i=m;i;i--) o[i].ans+=o[i+1].ans; for(i=1;i<=m;i++) { write(o[i].ans); putchar(10); } }
最終的版本實現方式:先二維偏序sort+樹狀數組解決(m,n]內的貢獻記在sum裏 ,最後給o[m+1].ans,並用兩次三維偏序解決[1,n]內部的貢獻,再跑一邊歸併計算出(m,n]對[1,n]的貢獻.逆序跑一遍後綴和後正序輸出.