%%%陈丹琦巨佬
当碰到一些动态的题目时,常常需要用到高级数据结构来维护,代码又臭又长。而在某些情况下,CDQ分治可以代替这些高级数据结构,转动态为静态来处理,使代码复杂度大大降低。
看到这个名称,就知道肯定是用分治的思想来解决了。
普通的分治中,左半个区间是不影响右半个区间的,做完当前区间直接递归。而CDQ分治一般会经过以下三个步骤:
1.将当前区间分成两半(废话)
2.考虑并计算左区间的所有操作对右区间的影响。
3.递归求解。
CDQ分治中最经典的问题就是三维偏序问题:
给出许多个有三个属性 (x,y,z) ( x , y , z ) 的元素,对于每个元素,求≤它的元素个数。( a≤b:a a ≤ b : a 的三个属性值分别小于 b b )
当只有两个属性时,把两个属性按照第一、二关键字从小到大排序,其实就是逆序对问题,可以用树状数组求解。
而有三个属性时,如果仍然用数据结构维护,同样把三个属性按照第一、二、三关键字排序,用树状数组维护第二个关键字,平衡树维护第三个关键字。即树状数组套平衡树。代码又臭又长。
我们考虑CDQ分治。把每个点看成一个数,所有点看成一个序列。先按三个关键字排完序后,对于一个分治的区间 [l,r] [ l , r ] ,再以 y y 为第一关键字,编号为第二关键字进行排序。这时 y y 不产生影响,直接用树状数组维护 z z 即可。把左区间的影响加上并计算答案,再撤销左区间的影响。最后递归求解。
以洛谷P3810(BZOJ3262)为例:
#include
#include
#include
#include
#define N 100005
#define F inline
using namespace std;
struct px{
int x,y,z,id;
bool operator == (const px &a) const{
return a.x==x&&a.y==y&&a.z==z;
}
}q[N],tem[N];
int n,k,t[N<<1],ans[N<<1],ti[N<<1];
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
if (l==r) return EOF; return *l++;
}
F int _read(){
int x=0,f=1; char ch=readc();
while (!isdigit(ch)) { if (ch=='-') f=-1; ch=readc(); }
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x*f;
}
F void writec(int x){ if (x>9) writec(x/10); putchar(x%10+48); }
F void _write(int x){ writec(x),putchar('\n'); }
//以上为IO优化
#define lbt(x) (x&(-x))
F void nsrt(int x,int p){ while (x<=k) t[x]+=p,x+=lbt(x); }
F int srch(int l,int r){
int x,sum=0; l--;
x=l; while (x) sum-=t[x],x-=lbt(x);
x=r; while (x) sum+=t[x],x-=lbt(x);
return sum;
}
//以上为树状数组单点修改,区间查询
F bool cmp1(px a,px b){ return a.yy||(a.y==b.y&&a.idx=(a.x==b.x),y=(a.y==b.y),z=(a.z==b.z);
return a.xx||(x&a.yy)||(x&y&a.zx&y&z&a.idq(int l,int r){
if (l==r) return; int mid=l+r>>1;
for (int i=l;i<=mid;i++) tem[i]=q[i],tem[i].id=0;
//把左区间的id赋成0,这样就可以保证左区间的影响
for (int i=mid+1;i<=r;i++) tem[i]=q[i];
sort(tem+l,tem+r+1,cmp1);
for (int i=l;i<=r;i++)
if (!tem[i].id) nsrt(tem[i].z,1);//加上影响
else ans[tem[i].id]+=srch(1,tem[i].z);//计算
for (int i=l;i<=r;i++) if (!tem[i].id) nsrt(tem[i].z,-1);//消去影响
cdq(l,mid),cdq(mid+1,r);//递归
}
//以上为CDQ分治
int main(){
n=_read(),k=_read();
for (int i=1;i<=n;i++)
q[i].x=_read(),q[i].y=_read(),q[i].z=_read(),q[i].id=i;
sort(q+1,q+n+1,cmp2);
for (int i=n-1,sum=0;i;i--)
ans[q[i].id]+=(q[i]==q[i+1]?++sum:sum=0);
cdq(1,n); for (int i=1;i<=n;i++) t[ans[i]]++;
for (int i=0;ireturn 0;
}