作用
优点是可以代替高级数据结构相互嵌套,而且十分好写.
缺点是必须离线.
实现方法
可以和分治辨析:
普通分治将一个大问题分成两个相互独立的子问题,去分别解决,而cdq分治用于解决一个子问题会去影响另外一个子问题的题目,比如说归并排序求逆序对就是cdq分治.
实现时先解决影响另一子问题的子问题(一般为左边的那部分),然后再解决右边的.
具体建议通过题目来理解.
例题
题面
题意
已知一个数列,你需要进行下面两种操作:
1.将某一个数加上x
2.求出某区间每一个数的和
解法
这题可以用树状数组秒杀,但也是练习cdq分治的非常好的一题.
首先将初值看作n次修改,询问区间和转化为两段前缀和的差,分别求出两段前缀和,可以按位置来进行归并排序(归并排序可以保证左边操作进行的时间全部小于右边),同时记录左边修改的和,同时修改右边的答案.
代码
#include<iostream>
#include<cstdio>
#define N 500100
using namespace std;
int n,m,cnt,ans[N],as;
struct Ope
{
int cz,a,b;
void make(int u,int v,int w)
{
cz=u,a=v,b=w;
}
} ope[N<<2],tmp[N<<2];
void solve(int l,int r)
{
if(l>=r) return;
int mid=((l+r)>>1),i=l,j=mid+1,sum=0,t=l;
solve(l,mid),solve(mid+1,r);
//统计左边,修改右边
for(; i<=mid&&j<=r;)
{
if(ope[i].a<=ope[j].a)
{
if(ope[i].cz==1)
sum+=ope[i].b;
tmp[t++]=ope[i++];
}
else
{
if(ope[j].cz==2)
ans[ope[j].b]+=sum;
else if(ope[j].cz==3)
ans[ope[j].b]-=sum;
tmp[t++]=ope[j++];
}
}
for(; i<=mid; tmp[t++]=ope[i++]);
for(; j<=r; tmp[t++]=ope[j++])
{
if(ope[j].cz==2)
ans[ope[j].b]+=sum;
else if(ope[j].cz==3)
ans[ope[j].b]-=sum;
}
for(i=l;i<=r;i++) ope[i]=tmp[i];
}
int main()
{
int i,j,o,p,q;
cin>>n>>m;
for(i=1; i<=n; i++)
{
scanf("%d",&p);
ope[++cnt].make(1,i,p);
}
for(i=1; i<=m; i++)
{
scanf("%d%d%d",&o,&p,&q);
if(o==1)
ope[++cnt].make(1,p,q);
else
{
ope[++cnt].make(3,p-1,++as);
ope[++cnt].make(2,q,as);
}
}
solve(1,cnt);
for(i=1; i<=as; i++)
printf("%d\n",ans[i]);
}
例题
以洛谷 P3810 【模板】三维偏序(陌上花开)为例
题意
给出n个三元组,f(i)表示n个三元组中三个数全部小于等于它的个数,求f(i)为0~n-1的个数.
做法
首先对第一维排序以忽略这一维的影响,之后剩下的两维,一维cdq分治,另外一维用树状数组维护一下即可.
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 100100
using namespace std;
int n,m,ans[N],sz[N<<1],mx,dy[N],an[N],ql[N*20],qq;
struct Num
{
int a,b,c,id;
bool operator < (const Num &u) const
{
if(c!=u.c) return c<u.c;
if(b!=u.b) return b<u.b;
return a<u.a;
}
bool operator == (const Num &u) const
{
return a==u.a&&b==u.b&&c==u.c;
}
} num[N],tmp[N];
inline int lb(int u){return u&(-u);}
inline void add(int u){for(; u<=m; u+=lb(u)) sz[u]++,ql[++qq]=u;;}
inline int ask(int u)
{
int res=0;
for(; u; u-=lb(u)) res+=sz[u];
return res;
}
inline void init()
{
register int i;
for(i=1;i<=qq;i++) sz[ql[i]]=0;
qq=0;
}
void cdq(int l,int r)
{
if(l>=r) return;
int mid=((l+r)>>1),i=l,j=mid+1,t=l;
cdq(l,mid),cdq(mid+1,r);
for(; i<=mid&&j<=r;)
{
if(num[i].b<=num[j].b)
{
add(num[i].a);
tmp[t++]=num[i++];
}
else
{
ans[num[j].id]+=ask(num[j].a);
tmp[t++]=num[j++];
}
}
for(; i<=mid; tmp[t++]=num[i++]);
for(; j<=r; tmp[t++]=num[j++]) ans[num[j].id]+=ask(num[j].a);
for(i=l; i<=r; i++) num[i]=tmp[i];
init();
}
int main()
{
int i,j;
cin>>n>>m;
for(i=1; i<=n; i++)
{
scanf("%d%d%d",&num[i].a,&num[i].b,&num[i].c);
dy[i]=i;
}
sort(num+1,num+n+1);
for(i=1;i<=n;i++) num[i].id=i;
for(i=n-1;i>=1;i--) if(num[i]==num[i+1]) dy[i]=dy[i+1];
cdq(1,n);
for(i=1;i<=n;i++) an[ans[dy[i]]]++;
for(i=0; i<n; i++) printf("%d\n",an[i]);
}