zoukankan      html  css  js  c++  java
  • CDQ分治·学习笔记

    CDQ分治学习笔记

    咕了好久,以前做过的好几道题都有关CDQ分治,但是都就抄了抄题解就扔了,没有怎么系统学过,今天学一下

    CQD分治简介

    二维偏序

    众所周之,分治有三个基本的步骤:划分子问题;解决问题;合并答案

    归并排序事分治

    任务1 分治的一道经典题是求逆序对个数(P1908)

    解:在归并排序的同时,处理答案(先排序再处理),从小到大排,当左指针的值>右指针的值时,代表分治的左区间中左指针右边的数都比右指针的值大,这就是逆序对啊,(ans+=(mid-t1+1))

    码:

    int op[N],n;
    void merge(int l,int r){
        int res[N];
        if(l==r) return;
        int mid=(l+r)>>1;
        merge(l,mid);
        merge(mid+1,r);
        int t1=l,t2=mid+1;
        for(int i=l;i<=r;i++){
            if((t1<=mid&&op[t1]<=op[t2])||t2>r){
                res[i]=op[t1];
                t1++;
            }else{
                res[i]=op[t2];
                ans+=(mid-t1+1);
                t2++;
            }
        }
        for(int i=l;i<=r;i++) op[i]=res[i];
        return;
    }
    

    这也叫做著名的二维偏序:

    给定(N)个有序对((a,b)),求对于每个((a,b)),满足(A<a)(B<b)的有序对((A,B))有多少个。

    这其实就是求顺序对,和上面一样

    这就叫CDQ分治

    二维偏序的扩展

    任务2 找出来树状数组的板子题,拿二维偏序写一写试试(P3374)

    解:把操作的时间看作a,把操作的位置看作b,a是有序的,对b进行cdq分治,求的是区间内操作对答案的贡献,但是⚠️注意:左区间的操作会对右区间的答案有影响,所以我们在分治的时候,左区间只处理修改,右区间只处理查询

    码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<string>
    #include<cmath>
    #include<vector>
    #include<map>
    #include<queue>
    #include<deque>
    #include<set>
    #include<stack>
    #include<bitset>
    #include<cstring>
    #define ll long long
    #define max(a,b) ((a>b)?a:b)
    #define min(a,b) ((a<b)?a:b)
    using namespace std;
    const int INF=0x3f3f3f3f,N=5000010;
    
    int n,m,cntx=0,cnt=0;
    
    struct node{
        int type,id;
        ll val;
        bool operator <(const node &a) const{
            if(id!=a.id) return id<a.id;
            else return type<a.type;
        }
    }a[N],b[N];
    
    ll ans[N];
    
    void cdq(int l,int r){
        if(l==r) return;
        int mid=(l+r)>>1;
        cdq(l,mid);
        cdq(mid+1,r);
        int t1=l,t2=mid+1;
        ll sum=0;
        for(int i=l;i<=r;i++){
            if((t1<=mid&&a[t1]<a[t2])||t2>r){
                if(a[t1].type==1) sum+=a[t1].val;
                b[i]=a[t1++];
            }else{
                if(a[t2].type==3) ans[a[t2].val]+=sum;
                else if(a[t2].type==2) ans[a[t2].val]-=sum;
                b[i]=a[t2++];
            }
        }
        for(int i=l;i<=r;i++) a[i]=b[i];
    }
    
    int main(){
        
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            cnt++;
            a[cnt].type=1;
            a[cnt].id=i;
            scanf("%lld",&a[cnt].val);
        }
        for(int i=1;i<=m;i++){
            int t;
            scanf("%d",&t);
            cnt++;
            a[cnt].type=t;
            if(t==1) scanf("%d%lld",&a[cnt].id,&a[cnt].val);
            else {
                int l,r;
                scanf("%d%d",&l,&r);
                cntx++;
                a[cnt].val=cntx;
                a[cnt].id=l-1;//前端点
                cnt++;
                a[cnt].type=3;
                a[cnt].val=cntx;
                a[cnt].id=r;//后端点
            }
        }
    
        cdq(1,cnt);
        for(int i=1;i<=cntx;i++) printf("%lld
    ",ans[i]);
        return 0;
    }
    

    三维偏序

    任务3 (P3810)

    给定(N)个有序三元组((a,b,c)),求对于每个三元组((a,b,c)),有多少个三元组((A,B,C))满足(A < a)(B < b)(C < c)

    解:最好的办法就是CDQ分治+数据结构,考虑可以比较权值大小的数据结构,可以用树状数组记录第三维,在保证序列前两维单调不下降的情况下,用代表权值的树状数组来处理答案

    码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<string>
    #include<cmath>
    #include<vector>
    #include<map>
    #include<queue>
    #include<deque>
    #include<set>
    #include<stack>
    #include<bitset>
    #include<cstring>
    #define ll long long
    #define max(a,b) ((a>b)?a:b)
    #define min(a,b) ((a<b)?a:b)
    using namespace std;
    const int INF=0x3f3f3f3f,N=200005;
    
    struct node{
        int a,b,c,cnt,ans;
    }s1[N],s2[N];
    
    int n,m,k,mx,top,su[N];
    int t[N];
    
    //用于第一次排序去重
    bool cmp1(node x,node y){
        if(x.a==y.a){
            if(x.b==y.b) return x.c<y.c;
            else return x.b<y.b;
        }else return x.a<y.a;
    }
    
    //用于区间内排序,排bc,保证区间内b维单调不下降
    bool cmp2(node x,node y){
        if(x.b==y.b) return x.c<y.c;
        else return x.b<y.b;
    }
    
    //树状数组
    int lowbit(int x){return x&(-x);}
    
    void add(int x,int y){
        while(x<=mx){
            t[x]+=y;
            x+=lowbit(x);
        }
    }
    
    int query(int x){
        int sum=0;
        while(x){
            sum+=t[x];
            x-=lowbit(x);
        }
        return sum;
    }
    
    //CDQ分治
    void cdq(int l,int r){
        if(l==r) return;
        int mid=(l+r)>>1;
        cdq(l,mid);
        cdq(mid+1,r);
        sort(s2+l,s2+mid+1,cmp2);
        sort(s2+1+mid,s2+r+1,cmp2);
        int i,j=l;
      	//双指针扫两个区间
        for(i=mid+1;i<=r;i++){
            while(s2[i].b>=s2[j].b&&j<=mid){
                add(s2[j].c,s2[j].cnt);
                j++;
            }
            s2[i].ans+=query(s2[i].c);
        }
      	//清空树状数组
        for(i=l;i<j;i++) add(s2[i].c,-s2[i].cnt);
    }
    
    int main(){
    
        scanf("%d%d",&n,&k);
        mx=k;
        for(int i=1;i<=n;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            s1[i].a=a;
            s1[i].b=b;
            s1[i].c=c;
        }
        sort(s1+1,s1+1+n,cmp1);
        for(int i=1;i<=n;i++){
            top++;
            if(s1[i].a!=s1[i+1].a||s1[i].b!=s1[i+1].b||s1[i].c!=s1[i+1].c){
                m++;
                s2[m].a=s1[i].a;
                s2[m].b=s1[i].b;
                s2[m].c=s1[i].c;
                s2[m].cnt=top;
                top=0;
            }
        }
        cdq(1,m);
        for(int i=1;i<=m;i++) su[s2[i].ans+s2[i].cnt-1]+=s2[i].cnt;
        for(int i=0;i<n;i++) printf("%d
    ",su[i]);    
        return 0;
    }
    

    CDQ分治例题

    lj给的题

    任务4 BZOJ2001

    CDQ分治+并查集

    任务5 BZOJ2244

    CDQ分治+dp

    任务6 BZOJ2989

    CDQ分治+数据结构

    任务7 BZOJ1942

    CDQ分治+斜率优化dp

    有时间再写

  • 相关阅读:
    Leetcode: 二分搜索法
    ALM 中查看某个 test 的更改 history 历史
    Python: map() and reduce()
    如何查询注册表的值及 Powershell 应用
    使用 Windows PowerShell 实现 Web UI 自动化 (转)
    zhuan: WAN simulating tool
    Robot Framework 1
    Enable Coded UI Testing of Your Controls
    NetBeans GUI tests on Jenkins + Windows (转)
    (转)JDK 1.8 预览版Lambda语法分析
  • 原文地址:https://www.cnblogs.com/wsyunine/p/14783298.html
Copyright © 2011-2022 走看看