CDQ用来解决分治时左半部分对右半部分造成影响的问题。
CDQ分治的经典问题是三维偏序问题。
要想解决三维偏序问题,首先你要知道什么是偏序。(废话)
一维偏序:
给出直线上的n个点,问有多少对点满足xi<=xj
对于这个问题,直接排序就可以了。
二维偏序:
给定平面内的n个点,问有多少对点满足xi<=xj且yi<=yj
这是个经典的树状数组问题,相信学过树状数组的人一定都做过·一道叫做数星星的题,这道题就是经典的二维偏序问题,并不需要二维数组,我们可以通过按x坐标为第一关键字排序,从而消除x坐标给答案带来的影响。然后我们用一个树状数组维护前缀和,记录之前有多少点的y坐标比该点小,由于在之前的x坐标一定比较小,因此只要保证y坐标即可。
三维偏序:
给出空间内的n个点,问有多少点对满足xi<=xj且yi<=yj且zi<=zj
树套树???码量++,空间++
在不强制在线的情况下,我们完全可以使用CDQ分治这种东西来简化一层树结构,因此三维偏序问题实际上可以这样处理:
1)仿照二位偏序问题,先给x排序,消除其影响。
2)使用CDQ分治,消除y的影响
3)使用数组维护z的前缀,统计答案。
1)、3)都是二维偏序的正常步骤,下面重点来讲讲CDQ分治
前边已经讲过了,CDQ分治要处理左区间对右区间的贡献问题。
其实很简单,我们把每一步操作分成三步:
1)递归处理左区间;2)递归处理右区间;3)处理左区间对右区间的贡献。
在三维偏序问题中,之所以不考虑右区间对左区间的贡献,是因为按x排序了,因此右区间一定不会对左区间造成贡献。
然后我们直接就按y排序,然后看一个东西是在左区间还是右区间,是左区间就放进一个树状数组中,否则直接求贡献。
因为CDQ直接分治到最小的单位,也就是一个点,因此可以保证所有答案都是没有遗漏的。
为了保证答案的不重复性,我们可以在求完贡献后把树状数组内贡献再撤消;
例题:洛谷P3810:陌上花开
参考代码如下:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<queue> 5 #define N 500005 6 #define lowbit(x) x&-x 7 using namespace std; 8 int read() 9 { 10 int x=0,f=1;char ch=getchar(); 11 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} 13 return x*f; 14 } 15 struct node{int x,y,z,id;}a[N]; 16 bool cmpa(node a,node b) 17 { 18 if(a.x!=b.x)return a.x<b.x; 19 if(a.y!=b.y)return a.y<b.y; 20 return a.z<b.z; 21 } 22 bool cmpb(node a,node b) 23 { 24 if(a.y!=b.y)return a.y<b.y; 25 if(a.x!=b.x)return a.x<b.x; 26 return a.z<b.z; 27 } 28 int n,k,tot,c[500005],b[500005],f[500005],s,j,block[500005]; 29 void modify(int x,int m){for(;x<=k;x+=lowbit(x))c[x]+=m;} 30 int query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;} 31 void cdq(int l,int r) 32 { 33 if(l==r)return; 34 int mid=(l+r)/2; 35 cdq(l,mid);cdq(mid+1,r); 36 sort(a+l,a+r+1,cmpb); 37 for(int i=l;i<=r;i++)(a[i].x<=mid)?modify(a[i].z,1),s=1:b[a[i].id]+=query(a[i].z); 38 for(int i=l;i<=r;i++)if(a[i].x<=mid)modify(a[i].z,-1); 39 } 40 int main() 41 { 42 ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); 43 cin>>n>>k;//n=read();k=read(); 44 for(int i=1;i<=n;i++) 45 { 46 cin>>a[i].x>>a[i].y>>a[i].z;//=read();a[i].y=read();a[i].z=read(); 47 a[i].id=i; 48 } 49 sort(a+1,a+1+n,cmpa); 50 for(int i=1;i<=n;) 51 { 52 j=i+1; 53 while(j<=n&&a[i].x==a[j].x&&a[i].y==a[j].y&&a[i].z==a[j].z)j++; 54 while(i<j)block[a[i].id]=a[j-1].id,i++; 55 } 56 for(int i=1;i<=n;i++)a[i].x=i; 57 cdq(1,n); 58 for(int i=1;i<=n;i++)f[b[block[a[i].id]]]++; 59 for(int i=0;i<n;i++)printf("%d ",f[i]); 60 return 0; 61 }