【cdq分治】【模板】
阿新 • • 發佈:2022-02-20
Problem
三維偏序的模板題。
Solution:
經典做法是,先排序控制一維,再用cdq分治控制一維,最後用資料結構(通常是樹狀陣列)維護一維。
cdq分治還可以巢狀,每巢狀一次,複雜度乘一個log,可以拓展到n維偏序(但超過3維,效率就很低了,通常會使用kd-tree),因此,這題也可以用兩個cdq分治巢狀解決(但我不會)
複雜度O(n log^2 n),cdq分治和樹狀陣列各提供一個log
需要注意的問題:
在cdq分治的外層排序中,必須要滿足在cdq內部劃分時,右半區間不能對左半區間造成影響,因此,當兩個元素完全相同時,需要把他們合成一個元素,最後再累計相同元素的貢獻。
Code
#include<bits/stdc++.h> using namespace std; inline int read() { register int x=0,w=1; register char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if(ch=='-') {ch=getchar();w=-1;} while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} return x*w; } inline void write(int x) { if(x<0) {putchar('-');x=~(x-1);} if(x>9) write(x/10); putchar('0'+x%10); } const int N=1e5+100; int n,k; struct node{ int a,b,c,s,ans; bool operator<(const node&x)const{ if(a!=x.a) return a<x.a; if(b==x.b) return c<x.c; return b<x.b; } bool operator==(const node&x)const{ return a==x.a&&b==x.b&&c==x.c; } }a[N]; int t[N<<1]; int cnt; bool cmp(node x,node y) { return x.b<y.b; } void add(int x,int c) { for(int i=x;i<=k;i+=i&-i) t[i]+=c; } int ask(int x) { int res=0; for(int i=x;i;i-=i&-i) res+=t[i]; return res; } 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,cmp);sort(a+mid+1,a+r+1,cmp); int pos=l; for(int i=mid+1;i<=r;++i) { while(pos<=mid&&a[pos].b<=a[i].b) { add(a[pos].c,a[pos].s);pos++; } a[i].ans+=ask(a[i].c); } for(int i=l;i<pos;++i) add(a[i].c,-a[i].s); } int d[N]; int main() { n=read();k=read(); for(int i=1;i<=n;++i){ a[i].a=read(); a[i].b=read(); a[i].c=read(); a[i].s=1; } sort(a+1,a+n+1); // int cnt=0; for(int i=1;i<=n;++i) if(a[i]==a[cnt]) a[cnt].s++; else a[++cnt]=a[i]; cdq(1,cnt); for(int i=1;i<=cnt;++i) d[a[i].ans+a[i].s-1]+=a[i].s; for(int i=0;i<n;++i){ write(d[i]);puts(""); } return 0; }
cdq分治的遞迴函式跟歸併排序十分類似,實際上,我們可以把cdq中的sort改為歸併排序,從而減少常數。
常數優化版本
#include<bits/stdc++.h> using namespace std; inline int read() { register int x=0,w=1; register char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if(ch=='-') {ch=getchar();w=-1;} while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} return x*w; } inline void write(int x) { if(x<0) {putchar('-');x=~(x-1);} if(x>9) write(x/10); putchar('0'+x%10); } const int N=1e5+100; int n,k; struct node{ int a,b,c,s,ans; bool operator<(const node&x)const{ if(a!=x.a) return a<x.a; if(b==x.b) return c<x.c; return b<x.b; } bool operator==(const node&x)const{ return a==x.a&&b==x.b&&c==x.c; } }a[N],tmp[N]; int t[N<<1]; int cnt; bool cmp(node x,node y) { return x.b<y.b; } void add(int x,int c) { for(int i=x;i<=k;i+=i&-i) t[i]+=c; } int ask(int x) { int res=0; for(int i=x;i;i-=i&-i) res+=t[i]; return res; } 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,cmp);sort(a+mid+1,a+r+1,cmp); int pos=l; for(int i=mid+1;i<=r;++i) { while(pos<=mid&&a[pos].b<=a[i].b) { add(a[pos].c,a[pos].s);pos++; } a[i].ans+=ask(a[i].c); } for(int i=l;i<pos;++i) add(a[i].c,-a[i].s); pos=l-1; int p=l,q=mid+1; while(p<=mid||q<=r) { if(p>mid) tmp[++pos]=a[q++]; else if(q>r) tmp[++pos]=a[p++]; else tmp[++pos]=a[a[p].b<a[q].b?p++:q++]; } for(int i=l;i<=r;++i) a[i]=tmp[i]; } int d[N]; int main() { n=read();k=read(); for(int i=1;i<=n;++i){ a[i].a=read(); a[i].b=read(); a[i].c=read(); a[i].s=1; } sort(a+1,a+n+1); // int cnt=0; for(int i=1;i<=n;++i) if(a[i]==a[cnt]) a[cnt].s++; else a[++cnt]=a[i]; cdq(1,cnt); for(int i=1;i<=cnt;++i) d[a[i].ans+a[i].s-1]+=a[i].s; for(int i=0;i<n;++i){ write(d[i]);puts(""); } return 0; }