為了反駁隔壁很對勁的太刀流,並不對勁的片手流決定與之針鋒相對,先一步發表cdq分治解三維偏序。
參照一、二維偏序的方法,會發現一位偏序就是直接排序,可以看成通過排序使第一維無效。二維偏序是排序+樹狀數組,就是先通過排序消除了第一維的影響,再通過樹狀數組進行統計。那么以此類推,三位偏序應該就是樹套樹狀數組…啊不對,是先通過排序消除第一維的影響,再通過【某種方法】消除第二維的影響,再用樹狀數組統計。
傳說中的【某種方法】就是cdq分治,它是一種通過計算前一半對后一半的影響的降維手段。
具體來說,假設三維分別是x,y,z,先按x排序。分治時每次將前半邊、后半邊分別按y排序。雖然現在x的順序被打亂了,但是前半邊還是都小於后半邊的,所以要是只計算前半邊對后半邊的偏序關系,是不會受到x的影響的。維護后一半的指針i,前一半的指針j,每次將i后移一位時,若y[j]<=y[i]則不斷后移j,並不斷將z[j]加入樹狀數組。然后再查詢樹狀數組中有多少數小於等於z[i]。 最后要清空樹狀數組。
還有“偏序問題中出現了完全相同的要把它們合並”、“清空樹狀數組時要減回去否則時間超限”、“前大括號必須放在下面“這些細節在此就不提了。
然后就會發現,一維偏序也可以cdq(雖然大部分人叫它歸並排序)、樹狀數組做,二維偏序也可以cdq做。也就是說,這些降維手段用在第幾維都可以。那么會不會有n維偏序,cdq套cdq什么的呢?據說那樣復雜度就會在n logkn,還不如n^2暴力枚舉。
其實cdq應該不會只局限於偏序問題,也許會有整體二分之類的的離線方法是參照cdq的算出前一半對后一半的影響這種思想呢?
不過強制在線就GG了。
垃圾太刀!!!

#include<iostream> #include<iomanip> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #define maxn 100010 #define maxk 200010 #define ll long long using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(isdigit(ch)==0 && ch!='-')ch=getchar(); if(ch=='-')f=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } inline void write(int x) { int f=0;char ch[20]; if(!x){puts("0");return;} if(x<0){putchar('-');x=-x;} while(x)ch[++f]=x%10+'0',x/=10; while(f)putchar(ch[f--]); putchar('\n'); } typedef struct node { int x,y,z,ans,w; }stnd; stnd a[maxn],b[maxn]; int n,cnt[maxk]; int k,n_; bool cmpx(stnd u,stnd v) { if(u.x==v.x) { if(u.y==v.y) return u.z<v.z; return u.y<v.y; } return u.x<v.x; } bool cmpy(stnd u,stnd v) { if(u.y==v.y) return u.z<v.z; return u.y<v.y; } struct treearray { int tre[maxk],kk; int lwbt(int x){return x&(-x);} int ask(int i){int ans=0; for(;i;i-=lwbt(i))ans+=tre[i];return ans;} void add(int i,int k){for(;i<=kk;i+=lwbt(i))tre[i]+=k;} }t; 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+mid+1,cmpy); sort(a+mid+1,a+r+1,cmpy); int i=mid+1,j=l; for(;i<=r;i++) { while(a[j].y<=a[i].y && j<=mid) t.add(a[j].z,a[j].w),j++; a[i].ans+=t.ask(a[i].z); } for(i=l;i<j;i++) t.add(a[i].z,-a[i].w); } int main() { n_=read(),k=read();t.kk=k; for(int i=1;i<=n_;i++) b[i].x=read(),b[i].y=read(),b[i].z=read(); sort(b+1,b+n_+1,cmpx); int c=0; for(int i=1;i<=n_;i++) { c++; if(b[i].x!=b[i+1].x || b[i].y!=b[i+1].y || b[i].z!=b[i+1].z ) a[++n]=b[i],a[n].w=c,c=0; } cdq(1,n); for(int i=1;i<=n;i++) cnt[a[i].ans+a[i].w-1]+=a[i].w; for(int i=0;i<n_;i++) write(cnt[i]); return 0; }
宣傳一波電教(現在是電教G了(現在是電教X了(現在是電教XX了))),歡迎加入。