您的位置:首页 > 其它

CDQ分治简介(洛谷P3810、BZOJ3262)

2018-02-12 15:23 295 查看
%%%陈丹琦巨佬

算法用途

当碰到一些动态的题目时,常常需要用到高级数据结构来维护,代码又臭又长。而在某些情况下,CDQ分治可以代替这些高级数据结构,转动态为静态来处理,使代码复杂度大大降低。

算法实现

基本步骤

看到这个名称,就知道肯定是用分治的思想来解决了。

普通的分治中,左半个区间是不影响右半个区间的,做完当前区间直接递归。而CDQ分治一般会经过以下三个步骤:

1.将当前区间分成两半(废话)

2.考虑并计算左区间的所有操作对右区间的影响。

3.递归求解。

经典问题

CDQ分治中最经典的问题就是三维偏序问题:

给出许多个有三个属性(x,y,z)(x,y,z)的元素,对于每个元素,求≤它的元素个数。(a≤b:aa≤b:a的三个属性值分别小于bb)

当只有两个属性时,把两个属性按照第一、二关键字从小到大排序,其实就是逆序对问题,可以用树状数组求解。

而有三个属性时,如果仍然用数据结构维护,同样把三个属性按照第一、二、三关键字排序,用树状数组维护第二个关键字,平衡树维护第三个关键字。即树状数组套平衡树。代码又臭又长。

我们考虑CDQ分治。把每个点看成一个数,所有点看成一个序列。先按三个关键字排完序后,对于一个分治的区间[l,r][l,r],再以yy为第一关键字,编号为第二关键字进行排序。这时yy不产生影响,直接用树状数组维护zz即可。把左区间的影响加上并计算答案,再撤销左区间的影响。最后递归求解。

模板

洛谷P3810BZOJ3262)为例:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#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
,tem
;
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.y<b.y||(a.y==b.y&&a.id<b.id); }
F bool cmp2(px a,px b){
bool x=(a.x==b.x),y=(a.y==b.y),z=(a.z==b.z);
return a.x<b.x||(x&a.y<b.y)||(x&y&a.z<b.z)||(x&y&z&a.id<b.id);
}
F void cdq(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;i<n;i++) _write(t[i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: