• CDQ分治--用时间降维的美丽算法


    CDQ分治–用时间降维的美丽算法


    CDQ分治,网上的阐述很多,太专业性的文字我就不赘述,这里指谈谈自己的感受

    还是%一下CDQ大神的论文

    CDQ分治的主要想法就是降维(比如三维问题降维到二维问题),并付出O(logn)的代价
    前提:支持离线


    那么我们思考一下经典二维偏序问题:
    给定数列a和b,问(i<j)满足ai<=aj,bi<=bj的组数
    我们把每一个i对应的ai,bi当做二维平面上的点
    并以x坐标为比较函数进行排序,使得对于任意i<j,满足ai<=aj,这样我们只需要讨论b的情况,但是暴力跑一遍显然是不够优秀的
    采用分治思想
    将区间[L,R]分成[L,mid][mid+1,R],先递归处理子问题,再考虑当前情况下左区间对右区间的贡献有多少
    把左右区间按照b的值排序,这个时候左区间所有点的a严格小于等于右区间所有数的a,所以我们可以直接双指针计算一下贡献
    每一层的时间效率都是O(n),一共有log层,于是时间复杂度是O(nlogn)


    思考一下如果是三维的情况怎么办?
    我们先对x排序,然后我们发现这样的问题转化成了二维问题,然后就可以套用二维偏序的方法了
    BZOJ3262

    #include<bits/stdc++.h>
    using namespace std;
    #define N 100010
    #define K 200010
    struct BIT{
        int t[K];
        void add(int x,int vl){
            while(x<K){
                t[x]+=vl;
                x+=x&(-x);
            }
        }
        int query(int x){
            int ans=0;
            while(x){
                ans+=t[x];
                x-=x&(-x);
            }
            return ans;
        }
    }T;
    struct Node{int a,b,c,cnt,ans;}p[N];
    int n,k,ans[N];
    inline bool cmp1(Node a,Node b){
        if(a.a==b.a&&a.b==b.b)return a.c<b.c;
        if(a.a==b.a)return a.b<b.b;
        return a.a<b.a;
    }
    inline bool cmp2(Node a,Node b){
        if(a.b==b.b)return a.c<b.c;
        return a.b<b.b;
    }
    void solve(int l,int r){
        if(l==r){p[l].ans=p[l].cnt;return;}
        int mid=(l+r)>>1;
        solve(l,mid);
        solve(mid+1,r);
        sort(p+l,p+mid+1,cmp2);
        sort(p+mid+1,p+r+1,cmp2);
        int tl=l,tr=mid+1;
        while(tr<=r){
            while(tl<=mid&&p[tl].b<=p[tr].b)T.add(p[tl].c,p[tl].cnt),tl++;
            p[tr].ans+=T.query(p[tr].c);
            tr++;
        }
        for(int i=l;i<tl;i++)T.add(p[i].c,-p[i].cnt);
    }
    int main(){
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&p[i].a,&p[i].b,&p[i].c);
        sort(p+1,p+n+1,cmp1);
        int newn=0;
        for(int i=1;i<=n;i++){
            if(p[i].a!=p[newn].a||p[i].b!=p[newn].b||p[i].c!=p[newn].c)p[++newn]=p[i],p[newn].cnt=1;
            else p[newn].cnt++;
        }
        swap(n,newn);
        solve(1,n);
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=n;i++)ans[p[i].ans]+=p[i].cnt;
        for(int i=1;i<=newn;i++)printf("%d
    ",ans[i]);
        return 0;
    } 
    

    但是如果增加了修改怎么办?
    这是时CDQ分治就变成了真·CDQ分治
    我们同样可以把问题递归并且只考虑当前增层的状态
    但是我们发现了修改这一神奇物质
    所以我们先很自然不可抗力地把原问题按照时间排序,。。。其实就是不动
    然后我们解决了时间的限制之后就可以在原问题上进行递归了
    思路大概是这样的:
    我们先将问题递归到左右子区间,分别统计之后我们只需要统计左区间修改对右区间查询的贡献(为什么没有右区间到左区间呢?),然后我们将左区间的修改和右区间的查询全部拿出来(感性理解一下),然后我们发现这个时候修改和查询又混在一起了,但是我们不用考虑时间关系只用考虑位置关系,所以我们就可以直接按照某一维的位置关系排一个序。。。
    然后我们就发现我们将原问题成功的降维了
    感觉贼优秀

    HDU5126
    本人博客

    /*HDU5126 CDQ分治*/
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define N 500010
    struct BIT{
        int t[N];
        void add(int x,int vl){for(;x<N;x+=x&(-x))t[x]+=vl;}
        int query(int x){int ans=0;for(;x;x-=x&(-x))ans+=t[x];return ans;}
    }T;
    struct Que{
        int x,y,z,id,typ,w;
        Que(){}
        Que(int _x,int _y,int _z,int _id,int _typ,int _w){
            x=_x,y=_y,z=_z,id=_id,typ=_typ,w=_w;
        }
    }q1[N<<3],q2[N<<3],q3[N<<3],q4[N<<3];
    int n,t,pre[N<<1],ans[N];
    vector<int> v;
    bool cmp1(Que a,Que b){
        if(a.x!=b.x)return a.x<b.x;
        return a.id<b.id;
    }
    bool cmp2(Que a,Que b){
        if(a.y!=b.y)return a.y<b.y;
        return a.id<b.id;
    }
    void solve2(int l,int r){
        if(l>=r)return;
        int mid=(l+r)>>1;
        solve2(l,mid);
        solve2(mid+1,r);
        int lenl=0,lenr=0;
        for(int i=l;i<=mid;i++)if(!q2[i].typ)q3[++lenl]=q2[i];
        for(int i=mid+1;i<=r;i++)if(q2[i].typ)q4[++lenr]=q2[i];
        sort(q3+1,q3+lenl+1,cmp2);
        sort(q4+1,q4+lenr+1,cmp2);
        int tl=1,tr=1;
        while(tr<=lenr){
            while(tl<=lenl&&q3[tl].y<=q4[tr].y)T.add(q3[tl].z,1),tl++;
            ans[q4[tr].id]+=q4[tr].w*T.query(q4[tr].z);
            tr++;
        }
        for(int i=1;i<tl;i++)T.add(q3[i].z,-1);
    }
    void solve1(int l,int r){//消除x维的影响
        if(l>=r)return;
        int mid=(l+r)>>1;
        solve1(l,mid);
        solve1(mid+1,r);
        int newq=0;
        for(int i=l;i<=mid;i++)if(!q1[i].typ)q2[++newq]=q1[i];
        for(int i=mid+1;i<=r;i++)if(q1[i].typ)q2[++newq]=q1[i];
        sort(q2+1,q2+newq+1,cmp1);
        solve2(1,newq);
    }
    int main(){
        //freopen("hdu5126.in","r",stdin);
        scanf("%d",&t);
        while(t--){
            v.clear();
            scanf("%d",&n);
            int cnt=0,tot=0;
            memset(ans,0,sizeof(ans));
            for(int i=1;i<=n;i++){
                int op;scanf("%d",&op);
                if(op==1){
                    cnt++;
                    scanf("%d%d%d",&q1[cnt].x,&q1[cnt].y,&q1[cnt].z);
                    q1[cnt].id=i;q1[cnt].typ=0;
                    pre[++tot]=q1[cnt].z;
                }else{
                    int x1,y1,z1,x2,y2,z2;
                    scanf("%d%d%d",&x1,&y1,&z1);
                    scanf("%d%d%d",&x2,&y2,&z2);
                    pre[++tot]=z1-1;
                    pre[++tot]=z2;
                    q1[++cnt]=Que(x2,y2,z2,i,1,1);
                    q1[++cnt]=Que(x1-1,y2,z2,i,1,-1);
                    q1[++cnt]=Que(x2,y1-1,z2,i,1,-1);
                    q1[++cnt]=Que(x2,y2,z1-1,i,1,-1);
                    q1[++cnt]=Que(x1-1,y1-1,z2,i,1,1);
                    q1[++cnt]=Que(x1-1,y2,z1-1,i,1,1);
                    q1[++cnt]=Que(x2,y1-1,z1-1,i,1,1);
                    q1[++cnt]=Que(x1-1,y1-1,z1-1,i,1,-1);
                    v.push_back(i);
                }
            }
            sort(pre+1,pre+tot+1);
            tot=unique(pre+1,pre+tot+1)-pre-1;
            for(int i=1;i<=cnt;i++)q1[i].z=lower_bound(pre+1,pre+tot+1,q1[i].z)-pre;
            solve1(1,cnt);//***cnt!=n
            for(int i=0;i<v.size();i++)printf("%d
    ",ans[v[i]]);
        }
        return 0;
    }
    
  • 相关阅读:
    Dom4j使用Xpath语法读取xml节点
    XML新手入门 创建构造良好的XML(2)
    XML新手入门 创建构造良好的XML(1)
    详解Java解析XML的四种方法
    Java中加载配置文件的集中方式,以及利用ClassLoader加载文件 .
    javaSE读取Properties文件的六种方法
    select
    socket
    socket异步通信-如何设置成非阻塞模式、非阻塞模式下判断connect成功(失败)、判断recv/recvfrom成功(失败)、判断send/sendto
    grep 多模式匹配
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9676327.html
Copyright © 2020-2023  润新知