cdq分治

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yzyyylx/article/details/79952645

作用

优点是可以代替高级数据结构相互嵌套,而且十分好写.
缺点是必须离线.

实现方法

可以和分治辨析:
普通分治将一个大问题分成两个相互独立的子问题,去分别解决,而cdq分治用于解决一个子问题会去影响另外一个子问题的题目,比如说归并排序求逆序对就是cdq分治.
实现时先解决影响另一子问题的子问题(一般为左边的那部分),然后再解决右边的.
具体建议通过题目来理解.

例题

题面

题意

已知一个数列,你需要进行下面两种操作:
1.将某一个数加上x
2.求出某区间每一个数的和

解法

这题可以用树状数组秒杀,但也是练习cdq分治的非常好的一题.
首先将初值看作n次修改,询问区间和转化为两段前缀和的差,分别求出两段前缀和,可以按位置来进行归并排序(归并排序可以保证左边操作进行的时间全部小于右边),同时记录左边修改的和,同时修改右边的答案.

代码

#include<iostream>
#include<cstdio>
#define N 500100
using namespace std;

int n,m,cnt,ans[N],as;
struct Ope
{
    int cz,a,b;
    void make(int u,int v,int w)
    {
        cz=u,a=v,b=w;
    }
} ope[N<<2],tmp[N<<2];

void solve(int l,int r)
{
    if(l>=r) return;
    int mid=((l+r)>>1),i=l,j=mid+1,sum=0,t=l;
    solve(l,mid),solve(mid+1,r);
    //统计左边,修改右边
    for(; i<=mid&&j<=r;)
    {
        if(ope[i].a<=ope[j].a)
        {
            if(ope[i].cz==1)
                sum+=ope[i].b;
            tmp[t++]=ope[i++];
        }
        else
        {
            if(ope[j].cz==2)
                ans[ope[j].b]+=sum;
            else if(ope[j].cz==3)
                ans[ope[j].b]-=sum;
            tmp[t++]=ope[j++];
        }
    }
    for(; i<=mid; tmp[t++]=ope[i++]);
    for(; j<=r; tmp[t++]=ope[j++])
    {
        if(ope[j].cz==2)
            ans[ope[j].b]+=sum;
        else if(ope[j].cz==3)
            ans[ope[j].b]-=sum;
    }
    for(i=l;i<=r;i++) ope[i]=tmp[i];
}

int main()
{
    int i,j,o,p,q;
    cin>>n>>m;
    for(i=1; i<=n; i++)
    {
        scanf("%d",&p);
        ope[++cnt].make(1,i,p);
    }
    for(i=1; i<=m; i++)
    {
        scanf("%d%d%d",&o,&p,&q);
        if(o==1)
            ope[++cnt].make(1,p,q);
        else
        {
            ope[++cnt].make(3,p-1,++as);
            ope[++cnt].make(2,q,as);
        }
    }
    solve(1,cnt);
    for(i=1; i<=as; i++)
        printf("%d\n",ans[i]);
}

例题

洛谷 P3810 【模板】三维偏序(陌上花开)为例

题意

给出n个三元组,f(i)表示n个三元组中三个数全部小于等于它的个数,求f(i)为0~n-1的个数.

做法

首先对第一维排序以忽略这一维的影响,之后剩下的两维,一维cdq分治,另外一维用树状数组维护一下即可.

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 100100
using namespace std;

int n,m,ans[N],sz[N<<1],mx,dy[N],an[N],ql[N*20],qq;
struct Num
{
    int a,b,c,id;
    bool operator < (const Num &u) const
    {
        if(c!=u.c) return c<u.c;
        if(b!=u.b) return b<u.b;
        return a<u.a;
    }
    bool operator == (const Num &u) const
    {
        return a==u.a&&b==u.b&&c==u.c;
    }
} num[N],tmp[N];

inline int lb(int u){return u&(-u);}
inline void add(int u){for(; u<=m; u+=lb(u)) sz[u]++,ql[++qq]=u;;}
inline int ask(int u)
{
    int res=0;
    for(; u; u-=lb(u)) res+=sz[u];
    return res;
}
inline void init()
{
    register int i;
    for(i=1;i<=qq;i++) sz[ql[i]]=0;
    qq=0;
}

void cdq(int l,int r)
{
    if(l>=r) return;
    int mid=((l+r)>>1),i=l,j=mid+1,t=l;
    cdq(l,mid),cdq(mid+1,r);
    for(; i<=mid&&j<=r;)
    {
        if(num[i].b<=num[j].b)
        {
            add(num[i].a);
            tmp[t++]=num[i++];
        }
        else
        {
            ans[num[j].id]+=ask(num[j].a);
            tmp[t++]=num[j++];
        }
    }
    for(; i<=mid; tmp[t++]=num[i++]);
    for(; j<=r; tmp[t++]=num[j++]) ans[num[j].id]+=ask(num[j].a);
    for(i=l; i<=r; i++) num[i]=tmp[i];
    init();
}

int main()
{
    int i,j;
    cin>>n>>m;
    for(i=1; i<=n; i++)
    {
        scanf("%d%d%d",&num[i].a,&num[i].b,&num[i].c);
        dy[i]=i;
    }
    sort(num+1,num+n+1);
    for(i=1;i<=n;i++) num[i].id=i;
    for(i=n-1;i>=1;i--) if(num[i]==num[i+1]) dy[i]=dy[i+1];
    cdq(1,n);
    for(i=1;i<=n;i++) an[ans[dy[i]]]++;
    for(i=0; i<n; i++) printf("%d\n",an[i]);
}
阅读更多

没有更多推荐了,返回首页