CDQ分治是一個好東西,一直聽着dalao們說因此就去學了下。git
CDQ分治是咱們處理各種問題的重要武器。它的優點在於能夠頂替複雜的高級數據結構,並且常數比較小;缺點在於必須離線操做。 ——by __stdcall數組
其實CDQ分治名字聽上去很高大上,其實和通常的分治沒有特別大的區別,其大致流程以下:數據結構
這裏特別注意CDQ分治與通常分治的區別:普通分治在合併兩個子問題的過程當中,左右區間內的問題不會互相影響。spa
咱們從一道模板題來看看CDQ的具體實現:Luogu P3810指針
首先考慮經典的二維偏序:逆序對code
這個鬼東西不是就一個歸併排序or權值樹狀數組的事情麼排序
咱們想一下歸併排序的原理,在歸併的過程當中(數組已經有序),那麼我左邊的而且座標大於右邊的座標個數其實就是逆序對個數。遞歸
所以這也算是個簡單的CDQ吧get
如今咱們考慮三維偏序,咱們考慮先對數組整體排個序,這樣在操做的過程當中總有\(a_i\le a_j(i<j)\)(即便咱們將區間一分爲二那麼右邊的數的\(a_i\)始終大於左邊。it
而後對於第二維\(y_i\),咱們考慮一下處理方法。
假設如今處理區間\([l,r]\),而此前咱們已經經過遞歸處理好了\([l,mid]\)和\([mid+1,r]\)的答案。
那咱們把\([l,mid]\)和\([mid+1,r]\)分別按\(y_i\)排個序,這樣第二維也有了上面的性質。
再考慮怎麼計算左邊和右邊的偏序關係,咱們能夠維護兩個指針\(i,j\),每次咱們將\(j\)後移一位以表示再加入一個數,此時若\(y_i\le y_j\)則不斷後移\(i\),而且將\(z_i\)加入權值樹狀數組。
而後如今對於右邊的每個數:
那麼只要找\(z_i\)小於它的數個數便可,這個咱們直接在樹狀數組上找便可。
複雜度是比較迷的\(O(n\log n)\),不過因爲CQD的常數很小因此能夠輕鬆跑過緬懷各位寫樹套樹的dalao
下面上CODE
#include<cstdio> #include<cctype> #include<algorithm> using namespace std; const int N=100005; struct data { int x,y,z,num,sum; bool operator ==(const data &s) const { return x==s.x&&y==s.y&&z==s.z; } }a[N],q[N]; int n,cnt,m,bit[N<<1],ans[N],tot; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1; while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag; } inline void write(int x) { if (x>9) write(x/10); putchar(x%10+'0'); } inline bool cmpx(data a,data b) { if (a.x==b.x&&a.y==b.y) return a.z<b.z; if (a.x==b.x) return a.y<b.y; return a.x<b.x; } inline bool cmpy(data a,data b) { if (a.y==b.y) return a.z<b.z; return a.y<b.y; } inline int lowbit(int x) { return x&-x; } inline void add(int x,int y) { for (;x<=m;x+=lowbit(x)) bit[x]+=y; } inline int get(int x) { int res=0; for (;x;x-=lowbit(x)) res+=bit[x]; return res; } inline void CDQ(int l,int r) { if (l==r) return; int mid=l+r>>1,id=l; CDQ(l,mid); CDQ(mid+1,r); sort(q+l,q+mid+1,cmpy); sort(q+mid+1,q+r+1,cmpy); for (register int i=mid+1;i<=r;++i) { while (id<=mid&&q[id].y<=q[i].y) add(q[id].z,q[id].num),++id; q[i].sum+=get(q[i].z); } for (register int i=l;i<id;++i) add(q[i].z,-q[i].num); } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); register int i; read(n); read(m); for (i=1;i<=n;++i) read(a[i].x),read(a[i].y),read(a[i].z); for (sort(a+1,a+n+1,cmpx),a[n+1]=(data){-1,-1,-1},i=cnt=1;i<=n;++i) if (a[i]==a[i+1]) ++cnt; else q[++tot]=a[i],q[tot].num=cnt,cnt=1; for (CDQ(1,tot),i=1;i<=tot;++i) ans[q[i].sum+q[i].num-1]+=q[i].num; for (i=0;i<n;++i) write(ans[i]),putchar('\n'); return 0; }
其實我也不會,不過對於通常的高維偏序,咱們能夠CDQ套CDQ,反正通常k維偏序用CDQ的複雜度就是\(O(n\log^{k-1} n)\)
所以維數太大時仍是使用K-d tree吧