CDQ分治

cdq分治一般是解决具有一下两种性质的问题(只有修改和查询操作):
1.只能用离线做法。
2.每个修改操作对询问的影响是独立的,即与之前的修改操作无关。

cdq分治与一般的分治不同,一般的分治分出来的子区间是独立的,个个击破即可,
而cdq分治分出来的两个子区间是相互联系的。(以下的分治都是指cdq分治)
由于在该问题中,每个询问只与在此之前的修改操作有关。
对于区间l,mid,记为区间1,区间mid+1,r,记为区间2,
过程:
1.递归处理区间(1),
2.递归处理区间(2),
3.统计区间1中的修改操作对区间2中的询问操作的影响。
4.算法结束。

 

CDQ分治模板题:https://blog.csdn.net/baodream/article/details/82666950

CDQ分治可求三维偏序,代码模板:

//CDQ分治,可用于求三维偏序,复杂度O(nlogn)
const int N = 2e5+5;

struct node{
    int x,y,z;
    int id;
}a[N],tmp[N];

int ans[N],cnt[N];   //ans[i]代表比第i个点小的点有多少个
int n,k;             //n代表点的个数,k代表点的范围

bool cmp(node a,node b){
    if(a.x==b.x&&a.y==b.y)
        return a.z<b.z;
    if(a.x==b.x)
        return a.y<b.y;
    return a.x<b.x;
}

int tree[N]; //tree数组按二进制存,根据n的末尾0的个数存取,树状数组

int lowbit(int x)
{
    return x&(-x);
}

int Query(int x)  //返回1到x的前缀和
{
    int res=0;
    while(x)
    {
        res+=tree[x];
        x-=lowbit(x);
    }
    return res;
}

void Add(int x,int v)  //实现a[x]+v;
{
    while(x<=k)        //注意这里是小于等于k,不是n,k是数据范围
    {
        tree[x]+=v;
        x+=lowbit(x);
    }
}

void clearr(int x){
    while(x<=k){
        if(tree[x]==0)
            break;
        tree[x]=0;
        x+=lowbit(x);
    }
}

void cdq(int l,int r){
     //cout<<"l "<<l<<" r "<<r<<endl;
     if(l>=r)
        return ;
    int mid = l+r>>1;
    cdq(l,mid);
    cdq(mid+1,r);
    int p=l,q=mid+1,k=l;
    while(p<=mid&&q<=r){
        if(a[p].y<=a[q].y){
            Add(a[p].z,1);
            tmp[k++] = a[p++];
        }
        else{
            ans[a[q].id]+=Query(a[q].z);
            tmp[k++] = a[q++];
        }
    }
    while(p<=mid){
        Add(a[p].z,1);
        tmp[k++] = a[p++];
    }
    while(q<=r){
        ans[a[q].id]+=Query(a[q].z);
        tmp[k++] = a[q++];
    }
    for(int i=l;i<=r;i++){
        clearr(a[i].z);
        a[i] = tmp[i];
    }
     /*
    cout<<"*** l "<<l<<" **  r "<<r<<endl;
    for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
    cout<<endl;
    */
}

bool compare(node a,node b){
    if(a.x==b.x&&a.y==b.y&&a.z==b.z)
        return true;
    return false;
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    std::ios::sync_with_stdio(false);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
        a[i].id = i;
    }
    sort(a+1,a+1+n,cmp);    //一维用sort保证,二维归并的时候保证,三维树状数组保证
    node temp = {-1,-1,-1,-1};
    int res = 0;
    for(int i=n;i>=1;i--){       //这里处理一下后面相同的个数,因为分治的时候处理不了后面的
        if(compare(temp,a[i])){
            ans[a[i].id]+=res;
            res++;
        }
        else{
            temp = a[i];
            res=1;
        }
    }
    cdq(1,n);        //cdq分治求答案
    /*for(int i=1;i<=n;i++)
        printf("ans[%d]=%d\n",i,ans[i]);*/
    for(int i=1;i<=n;i++)
        cnt[ans[i]]++;
    for(int i=0;i<n;i++)
        printf("%d\n",cnt[i]);  //cnt[i]是每一级的点的个数
    return 0;
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值