CDQ分治学习笔记

有关CDQ分治求解三维偏序问题的学习与思考

以BZOJ陌上花开一题为例

 

首先保证在进行分治处理前,使一维有序,可以简单利用sort函数实现

在分治过程中,左区间(l,mid)对于(mid+1,r)而言不存在逆序,从而将问题转化成二维偏序问题

只需要递归处理左右区间各自问题,对于两点跨过mid,各自在左右区间的情况

可以利用树状数组、归并排序求逆序对来处理运算,就可以把左区间对右区间的贡献计算出来

(一定是左区间对右区间的贡献,因为在处理区间问题时已经保证有序,右侧对左侧而言一定具有单调性)

 

故一般套路为:

假设三维偏序分别为a,b,c;

1、在main函数里保证a递增。

2、在CDQ里先分治左右,传下去的时候a仍然递增,不破坏性质。

3、分治完左右两边后,需保证左右两边分别b都是递增的(a不重要)。

4、归并排序,此时左边的a肯定都小于右边的a,那么如果对于一个右边的元素,之前类似归并的操作就可以保证所有小于b的左边的元素都已经遍历过。

5、第三维只需用树状数组等数据结构维护即可

归并后,发现统计完答案后b是有序递增的了(这个时候a已经不重要了)。对于上层操作,符合"左右两边分别b是递增的"了。

 

复杂度:设一次分治的复杂度是f(len),则复杂度是O(f(n)logn)。一般都在分治里用树状数组,一般的复杂度就是O(nlog2n)的。

 

正确性:按照线段树的形态递归的CDQ分治,保证每一对三元组在第一维划分的线段树上都有且仅有一个LCA,而这一组答案就会且仅会在LCA处计算。如果在LCA下面,点对不在一次分治内自然不会计算。如果在LCA上面了,点对就在同一侧,不会互相更新。

 

例题   BZOJ  陌上花开

题目描述

有n朵花,每朵花有三个属性:花形(s)、颜色(c)、气味(m),用三个整数表示。
现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。
定义一朵花A比另一朵花B要美丽,当且仅Sa>=Sb,Ca>=Cb,Ma>=Mb。显然,两朵花可能有同样的属性。需要统计出评出每个等级的花的数量。

输入格式

第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值。
以下N行,每行三个整数si, ci, mi (1 <= si, ci, mi <= K),表示第i朵花的属性

输出格式

包含N行,分别表示评级为0...N-1的每级花的数量。

样例

样例输入

10 3
3 3 3
2 3 3 
2 3 1 
3 1 1 
3 1 2 
1 3 1 
1 1 2 
1 2 2 
1 3 2 
1 2 1

样例输出

3
1
3
0
1
0
1
0
0
1
#include
#define re register
using namespace std;
const int L=1<<20|1;
char buffer[L],*S,*TT;
#define getchar() ((S==TT&&(TT=(S=buffer)+fread(buffer,1,L,stdin),S==TT))?EOF:*S++)
int n,m,cnt=0,tr[1000001],ans[100001];//sum 级别 tot 等级相同的个数
struct node{int a,b,c,sum,tot;}dat[100001],cal[100001];
inline int lowbit(re int x){return x&(-x);}
inline bool cmp(node a,node b){
    if(a.a!=b.a) return a.a<b.a;
    if(a.b!=b.b) return a.b<b.b;
    if(a.c!=b.c) return a.c<b.c;    
    return false;
}
inline bool Cmp(node a,node b){
    if(a.b!=b.b) return a.b<b.b;
    if(a.c!=b.c) return a.c<b.c;    
    if(a.a!=b.a) return a.a<b.a;
    return false;
}
inline int read(){
    re int a=0,b=1;re char ch=getchar();
    while(ch<'0'||ch>'9')
        b=(ch=='-')?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9')
        a=(a<<3)+(a<<1)+(ch^48),ch=getchar();
    return a*b;
}
inline void add(re int x,re int y){
    for(;x<=m;x+=lowbit(x)) tr[x]+=y;
}
inline int getsum(re int x){
    re int res=0;
    for(;x;x-=lowbit(x)) res+=tr[x];
    return res;
}
inline void cdq(re int l,re int r){
    if(l==r){cal[l].sum+=cal[r].tot-1;return ;}
    re int mid=(l+r)>>1;
    cdq(l,mid),cdq(mid+1,r);
    sort(cal+l,cal+mid+1,Cmp);
    sort(cal+mid+1,cal+r+1,Cmp);
    re int p=l;
    for(re int i=mid+1;i<=r;i++){
        while(p<=mid&&cal[p].b<=cal[i].b)
            add(cal[p].c,cal[p].tot),p++;
        cal[i].sum+=getsum(cal[i].c);
    }
    for(re int i=l;i)
        add(cal[i].c,-cal[i].tot);
}
signed main(){
    n=read(),m=read();
    for(re int i=1;i<=n;i++)
        dat[i].a=read(),dat[i].b=read(),dat[i].c=read(),dat[i].sum=1;
    sort(dat+1,dat+n+1,cmp);
    cal[++cnt]=dat[1],cal[cnt].tot=1;
    for(re int i=2;i<=n;i++){
        if(dat[i].a==dat[i-1].a&&dat[i].b==dat[i-1].b&&dat[i].c==dat[i-1].c)
            cal[cnt].tot++;
        else cal[++cnt]=dat[i],cal[cnt].tot=1;
    }
    cdq(1,cnt);
    sort(cal+1,cal+cnt+1,cmp);
    for(re int i=1;i<=cnt;i++)
        ans[cal[i].sum]+=cal[i].tot;
    for(re int i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}

你可能感兴趣的:(CDQ分治学习笔记)