CDQ分治简介(洛谷P3810、BZOJ3262)
程序员文章站
2022-05-14 18:48:08
...
%%%陈丹琦巨佬
算法用途
当碰到一些动态的题目时,常常需要用到高级数据结构来维护,代码又臭又长。而在某些情况下,CDQ分治可以代替这些高级数据结构,转动态为静态来处理,使代码复杂度大大降低。
算法实现
基本步骤
看到这个名称,就知道肯定是用分治的思想来解决了。
普通的分治中,左半个区间是不影响右半个区间的,做完当前区间直接递归。而CDQ分治一般会经过以下三个步骤:
1.将当前区间分成两半(废话)
2.考虑并计算左区间的所有操作对右区间的影响。
3.递归求解。
经典问题
CDQ分治中最经典的问题就是三维偏序问题:
给出许多个有三个属性的元素,对于每个元素,求≤它的元素个数。(的三个属性值分别小于)
当只有两个属性时,把两个属性按照第一、二关键字从小到大排序,其实就是逆序对问题,可以用树状数组求解。
而有三个属性时,如果仍然用数据结构维护,同样把三个属性按照第一、二、三关键字排序,用树状数组维护第二个关键字,平衡树维护第三个关键字。即树状数组套平衡树。代码又臭又长。
我们考虑CDQ分治。把每个点看成一个数,所有点看成一个序列。先按三个关键字排完序后,对于一个分治的区间,再以为第一关键字,编号为第二关键字进行排序。这时不产生影响,直接用树状数组维护即可。把左区间的影响加上并计算答案,再撤销左区间的影响。最后递归求解。
模板
#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[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.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;
}
推荐阅读
-
三维偏序(陌上花开)---洛谷P3810&&BZOJ3262(cdq分治--归并排序+树状数组)
-
CDQ分治简介(洛谷P3810、BZOJ3262)
-
洛谷P3810 陌上花开(CDQ分治)
-
洛谷P3810(陌上花开)(三维偏序,cdq分治)
-
洛谷 - P3810 【模板】三维偏序(陌上花开)(CDQ分治套树状数组)
-
洛谷 P3810 【模板】三维偏序(陌上花开)【cdq 分治】【树状数组】
-
【题解】洛谷P3810【模板】三维偏序(陌上花开)cdq分治+树状数组
-
洛谷P3810 【模板】三维偏序(陌上花开)【CDQ分治、树套树】
-
[bzoj] 3263 陌上花开 洛谷 P3810 三维偏序|| CDQ分治 && CDQ分治讲解
-
洛谷 P3810 【模板】三维偏序(陌上花开)cdq分治