首页 > 其他分享 >CDQ分治

CDQ分治

时间:2022-11-14 18:03:07浏览次数:48  
标签:cnt return int 分治 mid ++ CDQ

CDQ分治常用于解决多维偏序关系问题,以三维为例,第一维可以排序解决,第二维进行分治,第三位常用数据结构(树状数组)维护,于是也可以将动态带修改问题,离线处理,常默认时间为第一维。

由于许多时候同一个操作要分成多个操作来解决,所以要格外注意结构体数组的大小是否恰当,同时留意树状数组的边界。

CDQ优化dp时,通常是应用于1D/1Ddp问题,即dp数组是一维的,转移是1维的,可以从O(N2)优化到O(N*log2(N)2),分治时,若l=r则直接更新dp[l],否则先递归(l,mid),之后处理所有的l<=i<=mid对于mid+1<=j<=r的贡献,再递归(mid+1,r)

求sum(i=1->n)(sum(j=1->n))max(v[i],v[j])|x[i]-x[j]|.首先按照v升序排序,考虑左区间对右区间的贡献,设i<j,绝对值分为x[i]<x[j]和x[i]>x[j],按照x升序排序,找到第一个x[i]>x[j]的位置,j到左区间的v就是v[j],距离和就是j坐标坐标小于j的数的个数-坐标小于j的数的坐标和+坐标大于j的数的距离和-j坐标*坐标大于j的数的个数。

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,a=0,b=0;
    for(int i=l;i<=mid;i++)a+=q[i].y;
    for(;j<=r;j++){
        for(;i<=mid&&q[i].y<q[j].y;i++){
            a-=q[i].y;
            b+=q[i].y;
        }
        ans+=q[j].x*((i-l)*q[j].y-b+a-(mid-i+1)*q[j].y);
    }
    merge(q+l,q+mid+1,q+mid+1,q+1+r,pre+l,[](node a,node b){return a.y<b.y;});
    for(int i=l;i<=r;i++)q[i]=pre[i];
}

求有多少个连续子序列满足其和>=L且<=R.序列是连续的,可会议进行CDQ分治,满足限制l和r的下标连续递增,类加成前缀和,注意也要加入sum[0]。

void CDQ(int l,int r){
    if(l==r)return;
    int mid=l+r>>1;
    CDQ(l,mid);
    CDQ(mid+1,r);
    int h=l,t=l-1;
    for(int i=mid+1;i<=r;i++){
        while(t+1<=mid&&s[i]-s[t+1]>=liml)t++;/*找到第一个不合法的*/
        while(h<=mid&&s[i]-s[h]>limr)h++;/*找到第一个合法的*/
        ans+=t-h+1;
    }
    sort(s+l,s+r+1);
}

求一个序列有多少连续子序列满足平均值>=k.有平均值,则首先将序列所有值减去k,之后判断区间和即可,注意sum[0]也要计入。

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,sum=0;
    for(;j<=r;j++){
        for(;i<=mid&&s[j]-s[i]>=0;i++)sum++;
        ans+=sum;
    }
    sort(s+l,s+r+1);
}

一棵树,有两种点权a和b,以1为根,问每个点的子树内a和b权值均小于该点的点的数量。前两维两种点权偏序,第三维dfs序偏序,大的优先,这样就优先子树。

struct Ask{
    int red,blue,ipt,opt,id;
    inline bool friend operator<(Ask a,Ask b){
        return a.red==b.red?a.blue==b.blue?a.ipt>b.ipt:a.blue<b.blue:a.red<b.red;
    }
}q[N],pre[N];
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(;j<=r;j++){
        for(;i<=mid&&q[i].blue<=q[j].blue;i++)bit.add(q[i].ipt,1);
        ans[q[j].id]+=bit.ask(q[j].ipt,q[j].opt);
    }
    for(--i;i>=l;i--)bit.add(q[i].ipt,-1);
    merge(q+l,q+1+mid,q+1+mid,q+1+r,pre+l,[](Ask a,Ask b){return a.blue==b.blue?a.ipt>b.ipt:a.blue<b.blue;});
    for(int i=l;i<=r;i++)q[i]=pre[i];
}

有n个元素,每个元素有a,b,c三种属性,f(i)为a[j]<=a[i]&&b[j]<=b[i]&&c[j]<=c[i]的j的数量,求f(i)=d的数量,d属于0到n-1.三维分别是x,y,z,注意需要去重,树状数组的范围是z的范围,由于CDQ时还会对第二维进行排序,所以需要在结构体内部记录ans,这样排序是才会跟着该改变,而不能用单纯一个数组来记录。

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(;j<=r;j++){
        for(;i<=mid&&q[i].y<=q[j].y;i++)bit.add(q[i].z,q[i].w);
        q[j].ans+=bit.query(q[j].z);
    }
    for(--i;i>=l;i--)bit.add(q[i].z,-q[i].w);
    merge(q+l,q+mid+1,q+mid+1,q+1+r,pre+l,[](Ask a,Ask b){return a.y<b.y;});
    for(int i=l;i<=r;i++)q[i]=pre[i];
}
    for(int i=1;i<=n;i++){
        col++;
        if(q[i].x!=q[i+1].x||q[i].y!=q[i+1].y||q[i].z!=q[i+1].z)q[++m]=q[i],q[m].w=col,col=0;
    }
    CDQ(1,m);
    for(int i=1;i<=m;i++)ans[q[i].ans+q[i].w-1]+=q[i].w;
    for(int i=0;i<n;i++)cout<<ans[i]<<'\n';

维护一个支持单点修改和查询矩阵和的矩阵。对于一个数会对(1,1)到(x,y)做出贡献当且仅当横纵坐标和出现时间都小于当前询问,将询问差分,注意所欲坐标要+1来防止0.

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(;j<=r;j++){
        for(;i<=mid&&a[i].x<=a[j].x;i++)if(a[i].ask==0)bit.add(a[i].y,a[i].w);
        if(a[j].ask==1)a[j].w+=bit.query(a[j].y);
    }
    for(--i;i>=l;i--)if(a[i].ask==0) bit.add(a[i].y,-a[i].w);
    merge(a+l,a+mid+1,a+mid+1,a+r+1,preask+l);
    for(i=l;i<=r;i++)a[i]=preask[i];
}
    while(opt!=3){
        if(opt==1){
            int x,y,v;cin>>x>>y>>v;
            x++,y++;
            a[++cnt]={cnt,x,y,v,0};
        }
        else{
            int x1,y1,x2,y2;
            cin>>x1>>y1>>x2>>y2;x2++,y2++;
            a[++cnt]={cnt,x1,y1,0,1};
            a[++cnt]={cnt,x2,y2,0,1};
            a[++cnt]={cnt,x2,y1,0,1};
            a[++cnt]={cnt,x1,y2,0,1};
        }
        cin>>opt;
    }
    for(int i=1;i<=cnt;i++)if(a[i].ask==1)cout<<a[i].w+a[i+1].w-a[i+2].w-a[i+3].w<<'\n',i+=3;

单点标记,每次询问到距离询问最近的标记点的距离,距离为曼哈顿距离。考虑去掉曼哈顿距离的绝对值,设询问i,dis(i,j)=|x[i]-x[j]|+}y[i]-y[j]|,假设j在i的左下方,当x[j]+y[j]最大时dis最小,再满足时间轴要求即为三维偏序,四次CDQ,每次对应一个方向,都等效成查找距离当前点最近的左下方的点,maxx-x相当于左右翻转(查找右下),maxy-y相当于上下翻转(查找左上),两者同时做即查找右上,查询|x1-x2|+|y1-y2|,翻转后等效成查询(x1+y1)-(x2+y2),于是可以将x+y作为整体用树状数组维护最大值。

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(;j<=r;j++)
    if(!a[j].ask){
        for(;a[i].x<=a[j].x&&i<=mid;i++)
        if(a[i].ask)bit.add(a[i].y,a[i].x+a[i].y);
        int t=bit.query(a[j].y);
        if(t)ans[a[j].tim]=min(ans[a[j].tim],a[j].x+a[j].y-t);
    }
    for(j=l;j<i;j++)if(a[j].ask)bit.clear(a[j].y);
    merge(a+l,a+mid+1,a+mid+1,a+r+1,q+l);
    for(i=l;i<=r;i++)a[i]=q[i];
}
inline void del(){
    rx=ry=m=0;
    for(int i=1;i<=n;i++)if(!a[i].ask)rx=max(rx,a[i].x),ry=max(ry,a[i].y);
    for(int i=1;i<=n;i++)if(a[i].x<=rx&&a[i].y<=ry)q[++m]=a[i];
    for(int i=1;i<=m;i++)a[i]=q[i];
}
    for(int i=1;i<=n;i++)p[i]=a[i];
    del();CDQ(1,m);
    for(int i=1;i<=n;i++)a[i]=p[i],a[i].x=lx-a[i].x;
    del();CDQ(1,m);
    for(int i=1;i<=n;i++)a[i]=p[i],a[i].y=ly-a[i].y;
    del();CDQ(1,m);
    for(int i=1;i<=n;i++)a[i]=p[i],a[i].x=lx-a[i].x,a[i].y=ly-a[i].y;
    del();CDQ(1,m);

对一个排列,按照某种顺序依次删除m个元素,求每次删除之前的逆序对数。分成两部分考虑,tim[i]<tim[j]&&pos[i]<pos[j]&&v[i]>v[j]或tim[i]<tim[j]&&pos[i]>pos[j]&&v[i]<v[j],之后可以进行分治,再前缀和累加。

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(;j<=r;j++){
        for(;a[i].x<=a[j].x&&i<=mid;i++)bit.add(a[i].y,a[i].flag);
        ans[a[j].id]+=a[j].flag*bit.ask(a[j].y+1,n);
    }
    for(j=l;j<i;j++)bit.add(a[j].y,-a[j].flag);
    i=mid,j=r;
    for(;j>mid;j--){
        for(;a[i].x>=a[j].x&&i>=l;i--)bit.add(a[i].y,a[i].flag);
        ans[a[j].id]+=a[j].flag*bit.query(a[j].y-1);
    }
    for(j=i+1;j<=mid;j++)bit.add(a[j].y,-a[j].flag);
    merge(a+l,a+mid+1,a+mid+1,a+r+1,preask+l,cmp0);
    for(i=l;i<=r;i++)a[i]=preask[i];
}
    for(int i=1;i<=n;i++)cin>>b[i],pos[b[i]]=i,a[++cnt]={cnt,i,b[i],1,1,0};
    for(int i=1,x;i<=m;i++)cin>>x,a[++cnt]={cnt,pos[x],x,-1,-1,i};
    for(int i=1;i<=cnt;i++)preask[i]=a[i];
    CDQ(1,cnt);
    for(int i=1;i<=m;i++)ans[i]+=ans[i-1];
    for(int i=0;i<m;i++)cout<<ans[i]<<'\n';

一个序列,有的数可能会发生变化,但是同一时刻最多只有一个数发生变化,求一个子序列使得任意变化都是不降的。CDQ分治优化dp,对于x<y,当且仅当val[x]<=mi[y]&&ma[x]<=val[y]时可以满足题目要求(修改没有继承),CDQ时先将左区间按最大值升序排序,再将右区间按val升序排序,之后维护,最后再复原打乱的原数组(按pos升序排序)。

void CDQ(int l,int r){
    if(l==r)return f[l]=max(f[l],1),void();
    int mid=l+r>>1;
    CDQ(l,mid);
    sort(a+l,a+mid+1,[](node a,node b){return a.ma<b.ma;});
    sort(a+mid+1,a+r+1,[](node a,node b){return a.val<b.val;});
    int i=l,j=mid+1;
    for(;j<=r;j++){
        for(;a[i].ma<=a[j].val&&i<=mid;i++)bit.add(a[i].val,f[a[i].pos]);
        f[a[j].pos]=max(f[a[j].pos],bit.ask(a[j].mi)+1);
    }
    while(i>l)bit.clear(a[--i].val);
    sort(a+mid+1,a+r+1,[](node a,node b){return a.pos<b.pos;});
    CDQ(mid+1,r);
}

标签:cnt,return,int,分治,mid,++,CDQ
From: https://www.cnblogs.com/safeng/p/16889823.html

相关文章

  • CDQ分治
    0x00简介--是的,\(CDQ\)分治是一款由女oier\(CDQ\)引入的分治算法,可以利用分治让我们离线地解决一些在线数点问题--你说的对,但是面对强制在线的题目,\(C......
  • CDQ && 珂朵莉树
    对于题目:P4690[Ynoi2016]镜中的昆虫我们零基础从各个小部分开始学习,并且A了Ta.Part1CDQ分治一看到这个东西,一定会觉得很吓人,觉得是什么高大上的东西。其实不......
  • 线段树(Segment Tree)是一个基于分治的数据结构。
    线段树(SegmentTree)是一个基于分治的数据结构。线段树杂谈 概念:线段树(SegmentTree)是一个基于分治的数据结构。通常处理区间,序列中的查询,更改问题。大体上有单修,单......
  • 【模板】点分治 Centroid Decomposition
    postedon2022-07-2018:59:16|under模板|source0x00模板(P3806)给定\(n,k\)和一棵树,计算\[\sum\limits_{i,j\leqn}[{\ttdist}(i,j)=k]\]即树上距离为\(k\)......
  • API网关第三章-分治处理会话流程
    API网关第三章-分治处理会话流程一、前言这一章所做的就是让职责更加分明,Netty服务端只负责接受网络连接调用Session,Session单独拿出来去处理具体的调用逻辑。二、......
  • 点分治学习笔记
    点分治点分治用于求解树上路径有关的问题。其具体思想,对于当前处理的这一个分治区域,我们计算所有区域内跨过分治中心这一个点的所有路径的贡献,然后将分治中心及与其相邻......
  • 【ZJOI2007】捉迷藏(动态树分治)
    显然只有一次询问的话,可以用点分治来实现。但是现在我们有多组询问,还带有修改,我们只能通过动态点分治来做了。动态点分治的主要思想:省去每次点分治求重心的过程,直接预处......
  • 【XSY4746】会议选址(三度化,边分治,点分治)
    这种关键点的重心问题,一般有两种思路。一种是合并:对于两个不交的点集\(S,T\),\(S\cupT\)的重心必在\(S,T\)重心间的路径上,二分即可,需要数据结构支持dfn区间内\(S\c......
  • 【XSY3979】数据结构(分治,剪枝)
    题面数据结构题解挺神奇的一道题。正解是对\(y\)坐标分治。每次考虑\(y\)坐标在\([l,mid]\)范围内的红点和\(y\)坐标在\([mid+1,r]\)范围内的蓝点匹配成点......
  • 【XSY3972】树与图(树形dp,树剖,分治NTT)
    题面树与图题解不难发现本题可以转化成以下题目:给定一个\(n\)个点的有根树,你可以在树上选择\(k\)个点,满足对于任意两个点都不互为祖先关系,且从根到每个叶子的路......