zoukankan      html  css  js  c++  java
  • CDQ分治

      CDQ分治呢 是一种离线的分治算法 当然是基于“时间"的顺序对操作序列进行分治的。

    所以它也叫基于时间的分治算法。

    这个算法就只有三步:

    1 对于l r 先分而治之 mid solve(l,mid)

    2 再算 (mid+1,r)

    3 最后就是 (l,mid) 对 (mid+1,r)的贡献了。

    当然看似简单却让我有点难以理解 对于CDQ分治应该多理解这种离线想法。

    这个问题很显然是一个二维偏序问题。

    那么我们首先能够想到的是先维护单个元素单调然后在树状数组中

    维护另个元素单调查询答案即可。a,b那么大却一点都不重要,离散就可以了。

    CDQ呢 我们可以先按照也是先按照单个元素单调在一个序列之中 然后进行基于另一元素的分治。

    然后分而治之 然后完事注意细节在合并的时候 要注意再按照y从小到大的顺序再排。

    考虑到 mid+1~r是不可能对l~mid产生贡献的。所以我们可以这样做。

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<cstring>
    #include<string>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<cctype>
    #include<utility>
    #include<set>
    #include<bitset>
    #include<queue>
    #include<stack>
    #include<deque>
    #include<map>
    #include<vector>
    #include<ctime>
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void put(int x)
    {
        x<0?putchar('-'),x=-x:0;
        int num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar(10);return;
    }
    const int MAXN=200002;
    int n,m;
    struct wy
    {
        int x,y;
        int id;
        friend int operator < (const wy &l,const wy &r)
        {
            return l.x==r.x?l.y<r.y:l.x<r.x;
        }
    }t[MAXN],tmp[MAXN];//按照横坐标从小到大排序
    int ans[MAXN];
    void CDQ(int l,int r)
    {
        if(l==r)return;
        int mid=(l+r)>>1;
        CDQ(l,mid);
        CDQ(mid+1,r);
        int i=l,j=mid+1;
        for(int k=l;k<=r;k++)
        {
            if(j>r||(i<=mid&&t[i].y<=t[j].y))tmp[k]=t[i],i++;
            else ans[t[j].id]+=i-l,tmp[k]=t[j],j++;
        }
        for(int k=l;k<=r;k++)t[k]=tmp[k];
        return;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();
        for(int i=1;i<=n;i++)t[i].x=read(),t[i].y=read(),t[i].id=i;
        sort(t+1,t+1+n);
        CDQ(1,n);
        for(int i=1;i<=n;i++)put(ans[i]);
        return 0;
    }
    View Code

    这个题呢我想,还行吧跟上一个二维偏序差不多。CDQ分治一波吧。

    三维偏序有点坑啊,但是呢这依然和我上面想法一样不过在分治的时候

    加一维树状数组维护第三种元素的大小关系罢了。我想CDQ这种思想真的很不错呢!

    注意坑点 元素有可能为0 所以对于第三种元素整体加1.(还好没有完全相同3种的元素!)

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<cstring>
    #include<string>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<cctype>
    #include<utility>
    #include<set>
    #include<bitset>
    #include<queue>
    #include<stack>
    #include<deque>
    #include<map>
    #include<vector>
    #include<ctime>
    #define xx t[i].x
    #define yy t[i].y
    #define zz t[i].z
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline long long read()
    {
        long long x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void put(long long x)
    {
        x<0?putchar('-'),x=-x:0;
        long long num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar(10);return;
    }
    const long long MAXN=200002;
    long long n,m;
    struct wy
    {
        long long x,y,z;
        long long id;
    }t[MAXN],tmp[MAXN];
    long long ans[MAXN],c[MAXN],num[MAXN];
    void add(long long x,long long y)
    {
        for(;x<=m;x+=x&(-x))c[x]+=y;
    }
    long long ask(long long x)
    {
        long long cnt=0;
        for(;x;x-=x&(-x))cnt+=c[x];
        return cnt;
    }
    long long cmp(wy x,wy y)
    {
        if(x.x==y.x)
        {
            if(x.y==y.y)return x.z<y.z;
            return x.y<y.y;
        }
        return x.x<y.x;
    }
    void CDQ(long long l,long long r)
    {
        if(l==r)return;
        long long mid=(l+r)>>1;
        CDQ(l,mid);
        CDQ(mid+1,r);
        long long i=l,j=mid+1;
        for(long long k=l;k<=r;k++)
        {
            if(j>r||(i<=mid&&yy<=t[j].y))add(zz,1),tmp[k]=t[i],i++;
            else
            {
                ans[t[j].id]+=ask(t[j].z);
                tmp[k]=t[j];
                j++;
            }
        }
        for(long long k=l;k<=mid;k++)add(t[k].z,-1);
        for(long long k=l;k<=r;k++)t[k]=tmp[k];
        return;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();m=read();m++;
        for(long long i=1;i<=n;i++)
        {
            xx=read();yy=read();zz=read();
            t[i].id=i;
            zz++;
        }
        sort(t+1,t+1+n,cmp);
        //for(long long i=1;i<=n;i++)cout<<xx<<' '<<yy<<' '<<zz<<endl;
        CDQ(1,n);
        //for(long long i=1;i<=n;i++)put(ans[i]);
        for(long long i=1;i<=n;i++)num[ans[i]]++;
        for(long long i=0;i<n;i++)put(num[i]);
        return 0;
    }
    View Code

    这道题呢和上一道一样对不对 不过...

    范围也刚刚好对不对,关键没有坑点了k>=1坑点在于

    可能有几行元素完全相同,这就非常难受了好吧,我想判重吧。

    判重的话就非常难受了,至少我wa了一次发现自己是想错了,然后无数的表才过这题真不好写。

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<cstring>
    #include<string>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<cctype>
    #include<utility>
    #include<set>
    #include<bitset>
    #include<queue>
    #include<stack>
    #include<deque>
    #include<map>
    #include<vector>
    #include<ctime>
    #define xx s[i].x
    #define yy s[i].y
    #define zz s[i].z
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void put(int x)
    {
        x<0?putchar('-'),x=-x:0;
        int num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar(10);return;
    }
    const int MAXN=200002;
    int n,m;
    struct wy
    {
        int x,y,z;
        int id;
        int v;
    }t[MAXN],tmp[MAXN],s[MAXN];
    int ans[MAXN],c[MAXN],num[MAXN];
    int sum[MAXN];
    int cnt;
    void add(int x,int y)
    {
        for(;x<=m;x+=x&(-x))c[x]+=y;
    }
    int ask(int x)
    {
        int cnt=0;
        for(;x;x-=x&(-x))cnt+=c[x];
        return cnt;
    }
    int cmp(wy x,wy y)
    {
        if(x.x==y.x)
        {
            if(x.y==y.y)return x.z<y.z;
            return x.y<y.y;
        }
        return x.x<y.x;
    }
    void CDQ(int l,int r)
    {
        if(l==r)return;
        int mid=(l+r)>>1;
        CDQ(l,mid);
        CDQ(mid+1,r);
        int i=l,j=mid+1;
        for(int k=l;k<=r;k++)
        {
            if(j>r||(i<=mid&&t[i].y<=t[j].y))add(t[i].z,t[i].v),tmp[k]=t[i],i++;
            else
            {
                ans[t[j].id]+=ask(t[j].z);
                tmp[k]=t[j];
                j++;
            }
        }
        for(int k=l;k<=mid;k++)add(t[k].z,-t[k].v);
        for(int k=l;k<=r;k++)t[k]=tmp[k];
        return;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();m=read();
        for(int i=1;i<=n;i++)
        {
            xx=read();yy=read();zz=read();
            s[i].id=i;
        }
        sort(s+1,s+1+n,cmp);
        //for(int i=1;i<=n;i++)cout<<xx<<' '<<yy<<' '<<zz<<endl;
        t[++cnt]=s[1];t[cnt].v=1;sum[t[cnt].id]++;
        for(int i=2;i<=n;i++)
        {
            if(s[i].x==s[i-1].x&&s[i].y==s[i-1].y&&s[i-1].z==s[i].z)t[cnt].v++,sum[t[cnt].id]++;
            else t[++cnt]=s[i],t[cnt].v=1,sum[t[cnt].id]++;
        }
        //put(cnt);
        //for(int i=1;i<=cnt;i++)cout<<t[i].x<<' '<<t[i].y<<' '<<t[i].z<<' '<<t[i].v<<endl;
        //for(int i=1;i<=n;i++)cout<<sum[i]<<endl;
        CDQ(1,cnt);
        //for(int i=1;i<=cnt;i++)cout<<t[i].v<<endl;
        //for(int i=1;i<=cnt;i++)cout<<ans[i]<<endl;
        for(int i=1;i<=n;i++)num[ans[i]+sum[i]-1]+=sum[i];
        for(int i=0;i<n;i++)put(num[i]);
        return 0;
    }
    View Code

    关于偏序问题大部分都可以使用CDQ分治。

    下面转载一个博主队偏序问题的理解理解的非常到位呢!

    参照一、二维偏序的方法,会发现一位偏序就是直接排序,可以看成通过排序使第一维无效。二维偏序是排序+树状数组,就是先通过排序消除了第一维的影响,再通过树状数组进行统计。那么以此类推,三位偏序应该就是树套树状数组…啊不对,是先通过排序消除第一维的影响,再通过【某种方法】消除第二维的影响,再用树状数组统计。

    传说中的【某种方法】就是cdq分治,它是一种通过计算前一半对后一半的影响的降维手段。

    具体来说,假设三维分别是x,y,z,先按x排序。分治时每次将前半边、后半边分别按y排序。虽然现在x的顺序被打乱了,但是前半边还是都小于后半边的,所以要是只计算前半边对后半边的偏序关系,是不会受到x的影响的。维护后一半的指针i,前一半的指针j,每次将i后移一位时,若y[j]<=y[i]则不断后移j,并不断将z[j]加入树状数组。然后再查询树状数组中有多少数小于等于z[i]。 最后要清空树状数组。

    还有“偏序问题中出现了完全相同的要把它们合并”、“清空树状数组时要减回去否则时间超限”、“前大括号必须放在下面“这些细节在此就不提了。

    然后就会发现,一维偏序也可以cdq(虽然大部分人叫它归并排序)、树状数组做,二维偏序也可以cdq做。也就是说,这些降维手段用在第几维都可以。那么会不会有n维偏序,cdq套cdq什么的呢?据说那样复杂度就会在n logkn,还不如n^2暴力枚举。

    其实cdq应该不会只局限于偏序问题,也许会有整体二分之类的的离线方法是参照cdq的算出前一半对后一半的影响这种思想呢?

    不过强制在线就GG了。

  • 相关阅读:
    栈区,堆区,全局区,文字常量区,程序代码区 详解
    2010年IT行业十大收购
    三大数据备份方式:完全备份、增量备份以及差异备
    Driver Development Part 1: Introduction to Drivers (code project)
    手工构造典型PE文件(转)
    访问IIS元数据库失败[转]
    代码注入的三种方法(转)
    对象的初始化(转)
    网络和黑客编程基本知识 (转)
    破解linux中root密码(图) 转自csdn
  • 原文地址:https://www.cnblogs.com/chdy/p/10431924.html
Copyright © 2011-2022 走看看