zoukankan      html  css  js  c++  java
  • cdq分治学习

    经典问题:三维偏序

    题:https://www.luogu.org/problem/P3810

    一般处理:先按照自己定义的第一维排好序,那么在接下来的俩维判断中,我们就可以消除第一维造成的影响,接着用以前学过的分治排序法来处理第二维,以第二维作为排序对象,对于分治的[l,midd]和[midd+1,r]这俩个区间

    对于区间 [m+1, r]中的某个元素x,将区间 [l, m] 的第二维小于x的元素的按第三维的权值加入树状数组,

    最后区间 [l, m] 对区间 x 的贡献就是查询树状数组中小于x第三维的个数

    可以边进行分治边进行归并排序,树状数组要及时清空

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    inline int read(){
        int sum=0,x=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){
            if(ch=='-')
                x=0;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
            sum=(sum<<1)+(sum<<3)+(ch^48),ch=getchar();
        return x?sum:-sum;
    }
    inline void write(ll x){
        if(x<0)
            putchar('-'),x=-x;
        if(x>9)
            write(x/10);
        putchar(x%10+'0');
    }
    #define pb push_back
    #define lowbit(x) (x&(-x))
    const int M=2e5+5;
    struct node{
        int x,y,z,cnt,ans;
        bool operator < (const node &b)const{
            if(x!=b.x)
                return x<b.x;
            else if(y!=b.y)
                return y<b.y;
            else
                return z<b.z;
        }
    }a[M>>1],temp[M>>1];
    int tree[M];
    int n,k;
    int ANS[M];
    void add(int i,int x){
        while(i<=k){
            tree[i]+=x;
            i+=lowbit(i);
        }
    }
    int query(int i){
        int res=0;
        while(i){
            res+=tree[i];
            i-=lowbit(i);
        }
        return res;
    }
    void cdq(int l,int r){
        if(l==r){
            a[l].ans+=a[l].cnt-1;
            return ;
        }
        int midd=(l+r)>>1;
        cdq(l,midd);
        cdq(midd+1,r);
        int i=l,j=midd+1,op=l;
        while(j<=r){
            while(i<=midd&&a[i].y<=a[j].y){
                add(a[i].z,a[i].cnt);
                temp[op++]=a[i];
                i++;
            }
            a[j].ans+=query(a[j].z);
            temp[op++]=a[j];
            j++;
        }
        for(j=l;j<i;j++)
            add(a[j].z,-a[j].cnt);
        while(i<=midd)
            temp[op++]=a[i],i++;
        
        for(i=l;i<=r;i++)
            a[i]=temp[i];
    }
    int main(){
        n=read(),k=read();
        for(int i=1;i<=n;i++)
            a[i].x=read(),a[i].y=read(),a[i].z=read();
        sort(a+1,a+1+n);
        int now=1,cnt=0;
        for(int i=2;i<=n;i++){
            if(a[i-1].x==a[i].x&&a[i-1].y==a[i].y&&a[i-1].z==a[i].z)
                now++;
            else{
                a[++cnt]=a[i-1];
                a[cnt].cnt=now;
                a[cnt].ans=0;
                now=1;
            }
            
        }
        a[++cnt]=a[n];
        a[cnt].cnt=now;
        a[cnt].ans=0;
        cdq(1,cnt);
        for(int i=1;i<=cnt;i++)
            ANS[a[i].ans]+=a[i].cnt;
        for(int i=0;i<n;i++)
            printf("%d
    ",ANS[i]);
        return 0;    
    }
    View Code

     题:http://acm.hdu.edu.cn/showproblem.php?pid=5618

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #define lowbit(i) i&(-i)
    using  namespace std;
    typedef long long ll;
    const int M=1e5+5;
    int tree[M<<1],ans[M];
    int n;
    struct node{
        int x,y,z;
        int id,ans,cnt;
        bool operator < (const node &b)const{
            if(x!=b.x)
                return x<b.x;
            else if(y!=b.y)
                return y<b.y;
            else
                return z<b.z;
        }
    }a[M],temp[M];
    void add(int pos,int val){
        while(pos<M)
            tree[pos]+=val,pos+=lowbit(pos);
    }
    int query(int pos){
        int res=0;
        while(pos)
            res+=tree[pos],pos-=lowbit(pos);
        return res;
    }
    bool cmp1(node p,node q){
        return p.x<q.x;
    }
    bool cmp2(node p,node q){
        return p.id<q.id;
    }
    void cdq(int l,int r){
        int midd=(l+r)>>1;
        if(l==r){
            return ;
        }
    
        cdq(l,midd);
        cdq(midd+1,r);
        int i=l,j=midd+1,m=l;
        while(j<=r){
            while(i<=midd&&a[i].y<=a[j].y){
                add(a[i].z,a[i].cnt);
                temp[m++]=a[i];
                i++;
            }
            ans[a[j].id]+=query(a[j].z);
            temp[m++]=a[j];
            j++;
        }
        for(int p=l;p<i;p++)
            add(a[p].z,-a[p].cnt);
        while(i<=midd)
            temp[m++]=a[i],i++;
        for(int p=l;p<=r;p++)
            a[p]=temp[p];
    
    }
    int main(){
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            for(int i=1;i<=n;i++)
                ans[i]=0;
            for(int i=1;i<=n;i++)
                scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z),a[i].cnt=1,a[i].id=i;
            sort(a+1,a+1+n);
            int now=0;
            for(int i=n-1;i>=1;i--){
                if(a[i].x==a[i+1].x&&a[i].y==a[i+1].y&&a[i].z==a[i+1].z)
                    now++;
                else
                    now=0;
                ans[a[i].id]+=now;
            }
            cdq(1,n);
            for(int i=1;i<=n;i++)
                printf("%d
    ",ans[i]);
        }
        return 0;
    }
    View Code

    例:https://codeforces.com/problemset/problem/669/E

    题意:没给定的三个数分别表示操作,时刻(在t时刻进行这项操作),val

         增加或减去这个val的个数一次,查询val的个数

    分析:一开始误以为是按照时间来排序然后用单点更新,单点查询操作就行了,其实不然,他是按照给定顺序进行操作的,时间可以看出多一个维度(这可以通过下面的样例模拟一下)

       /*

    10
    1 1 1000000000
    1 4 1000000000
    2 2 1000000000
    1 5 1000000000
    1 8 1000000000
    2 15 1000000000
    3 3 1000000000
    3 10 1000000000
    3 6 1000000000
    3 7 1000000000

    answer output
    0
    3
    2
    2
    */
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pb push_back
    #define lowbit(x) (x&(-x))
    #define lson root<<1,l,midd
    #define rson root<<1|1,midd+1,r
    inline int read(){
        int sum=0,x=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){
            if(ch=='-')
                x=0;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
            sum=(sum<<1)+(sum<<3)+(ch^48),ch=getchar();
        return x?sum:-sum;
    }
    inline void write(ll x){
        if(x<0)
            putchar('-'),x=-x;
        if(x>9)
            write(x/10);
        putchar(x%10+'0');
    }
    
    const int M=1e5+5;
    int ans[M];
    struct node{
        int op,ti,val,cnt,id;
        bool operator< (const node &b)const{
            return ti<b.ti;
        }
    }a[M],temp[M];
    int lisan[M],m;
    map<ll,ll>countt;
    void cdq(int l,int r){
        if(l==r)
            return ;
        int midd=(l+r)>>1;
        cdq(l,midd);
        cdq(midd+1,r);
        int i=l,j=midd+1,mm=l;
        while(j<=r){
            while(i<=midd&&a[i].ti<=a[j].ti){
                if(a[i].op!=3){
                    countt[a[i].val]+=a[i].cnt;
                }
                temp[mm++]=a[i];
                i++;
            }
            if(a[j].op==3)
                ans[a[j].id]+=countt[a[j].val];
            temp[mm++]=a[j];
            j++;
        }
        for(j=l;j<i;j++)
            if(a[j].op!=3)
                countt[a[j].val]-=a[j].cnt;
        while(i<=midd)
            temp[mm++]=a[i],i++;
        for(i=l;i<=r;i++)
            a[i]=temp[i];
        return ;
    }
    int main(){
        int n=read();
        int num=0,tot=0;
        for(int i=1;i<=n;i++){
            a[i].op=read();
            a[i].ti=read();
            a[i].val=read();
            if(a[i].op==3)
                a[i].id=++tot,a[i].cnt=0;
            else if(a[i].op==1)
                a[i].cnt=1;
            else
                a[i].cnt=-1;
            lisan[++num]=a[i].val;
        }
        sort(lisan+1,lisan+1+num);
        
        m=unique(lisan+1,lisan+1+num)-lisan-1;
        for(int i=1;i<=n;i++)
            a[i].val=lower_bound(lisan+1,lisan+1+num,a[i].val)-lisan;
        cdq(1,n);
        for(int i=1;i<=tot;i++)
            printf("%d
    ",ans[i]);
        return 0;
    }
    View Code

     题:https://www.luogu.org/problem/P3157

    P3157 [CQOI2011]动态逆序对

    删除一个数他减少的逆序数个数等于他前面比它大的和他后面比它小的之和,为了不造成删了当前这个点对后面删的点的影响,所以要从后面开始删,这里用这个删的次序作为排序的一维

    所以当前的三维:位置,这个位置的值,这个位置删的次序

    三维偏序用cdq分治

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define lowbit(pos) pos&(-pos)
    int n,m;
    const int M=1e5+5;
    ll ans[M];
    int pos[M];
    struct NODE{
        int pos,id,v,t;
        bool operator <(const NODE &b){
            return t<b.t;
        }
    }a[M],temp[M];
    struct TREE{
        int tree[M];
        void init(){
            for(int i=0;i<=n;i++)
                tree[i]=0;
        }
        void add(int pos,int c){
            while(pos<=n)
                tree[pos]+=c,pos+=lowbit(pos);
        }
        int sum(int pos){
            int res=0;
            while(pos){
                res+=tree[pos];
                pos-=lowbit(pos);
            }
            return res;
        }
    }T;
    void cdq1(int l,int r){
        int midd=(l+r)>>1;
        if(l==r)
            return ;
        cdq1(l,midd);
        cdq1(midd+1,r);
        int p=l,q=midd+1,tp=l;
        while(q<=r){
            while(p<=midd&&a[p].pos<a[q].pos){
                T.add(a[p].v,1);
                temp[tp++]=a[p];
                p++;
            }
            ans[a[q].id]+=T.sum(n)-T.sum(a[q].v);
            temp[tp++]=a[q];
            q++;
        }
        for(int i=l;i<p;i++)
            T.add(a[i].v,-1);
        while(p<=midd)
            temp[tp++]=a[p],p++;
        for(int i=l;i<=r;i++)
            a[i]=temp[i];
    }
    void cdq2(int l,int r){
        if(l==r)
            return ;
        int midd=(l+r)>>1;
        cdq2(l,midd);
        cdq2(midd+1,r);
        int p=l,q=midd+1,tp=l;
        while(q<=r){
            while(p<=midd&&a[p].pos>a[q].pos){
                T.add(a[p].v,1);
                temp[tp++]=a[p];
                p++;
            }
            ans[a[q].id]+=T.sum(a[q].v-1);
            temp[tp++]=a[q];
            q++;
        }
        for(int i=l;i<p;i++)
            T.add(a[i].v,-1);
        while(p<=midd)
            temp[tp++]=a[p],p++;
        for(int i=l;i<=r;i++)
            a[i]=temp[i];
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i].v);
            a[i].pos=i;
            a[i].t=0;
            a[i].id=0;
            pos[a[i].v]=i;
        }
        ll res=0ll;
        for(int i=1;i<=n;i++){
            res+=T.sum(n)-T.sum(a[i].v);
            T.add(a[i].v,1);
        }
        T.init();
        for(int x,i=1;i<=m;i++){
            scanf("%d",&x);
            a[pos[x]].t=m-i+1;
            a[pos[x]].id=i;
        }
        ///删除一个数对逆序数的影响为减去这个数前面比他大的和后面比他小之和
        //先算前面比他大的 
        sort(a+1,a+1+n);
        cdq1(1,n);
        //算后面比他小的 
        sort(a+1,a+1+n);
        cdq2(1,n);
        for(int i=1;i<=m;i++){
            printf("%lld
    ",res);
            res-=ans[i];
        } 
        return 0;
    }
    View Code
  • 相关阅读:
    初识nginx
    Keepalived 配置实例
    ssh学习小记
    代码开发、测试及发布
    需求改进&系统设计
    软件设计原则、设计模式学习+部分实现
    自我介绍+课程 6 问
    python函数嵌套出现报错UnboundLocalError原理的猜测(有解决办法,但是对于报错原理不确定)
    python tkinter 问题(多个Listbox选取显示问题,虚拟事件的特点为何虚拟,listbox.nearest函数与虚拟事件绑定返回值错误,StringVar类参数调用时单向性,线程无响应)
    python tkinter pack布局遇到的错误和问题总结(无图)
  • 原文地址:https://www.cnblogs.com/starve/p/11628083.html
Copyright © 2011-2022 走看看