• CDQ分治小结



    基础知识

    CDQ分治的优缺点:

    ( 1 )优点:代码量少,常数极小(@树套树),可以降低处理维数。(如果嵌套可以大段蒯
    ( 2 )缺点:必须离线处理

    本质:

    分治,只考虑 [L,mid]中元素对 [mid+1,R] 中元素的影响
    (这意味着左边只修改,右边只统计答案,不会证正确性

    写法:

    据说写法很多,但我只会一种。。。
    最后一维

    step 1:分治
    step 2:边处理边归并排序(左边插入,把能影响当前答案的插完后统计一个右边答案)
    step 3:复原(清零)
    

    前面几维

    step 1:分治
    step 2:排序(归并排序)
    step 3:往下一维CDQ
    

    关于使用

    一层CDQ有时可以代替一层数据结构
    如何CDQ套CDQ?
    留坑,到时候yy一下结构和分治的时机

    例题

    T1

    Luogu3368 【模板】树状数组 2
    第一维排序,第二维CDQ
    懒得打了,蒯下面那个板板就成

    T2

    Luogu3810 【模板】三维偏序(陌上花开)
    第一维排序,第二维CDQ,第三维值域树状数组
    (理论上可以打两层CDQ,但常数大)
    贴个板板养眼,不记得背背就成

    il void CDQ(re int L,re int R)
    {
      if(L==R) return;
      re int mid=L+R>>1;
      CDQ(L,mid);CDQ(mid+1,R);
      re int l=L,r=mid+1,top=L-1;
      while(l<=mid&&r<=R)
        {
          if(q[l].b<=q[r].b) add(q[l].c,q[l].num),q1[++top]=q[l++];//三维偏序优先左边,因此一定要加等号
          else q[r].ans+=Query(q[r].c),q1[++top]=q[r++];
        }
      while(l<=mid) q1[++top]=q[l++];
      while(r<=R) q[r].ans+=Query(q[r].c),q1[++top]=q[r++];
      fp(i,L,mid) Clear(q[i].c);fp(i,L,R) q[i]=q1[i];
    }
    

    Update:友情附赠树套树版本

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define re register
    #define il inline
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=5e5+100;
    struct node
    {
      int a,b,c,s,ans;
      bool operator < (const node &o) const {return a<o.a||(a==o.a&&b<o.b)||(a==o.a&&b==o.b&&c<o.c);}
      bool operator == (const node &o) const {return a==o.a&&b==o.b&&c==o.c;}
    }q[N],a[N];
    struct seg{int ls,rs,s,sum;}t[N*20];
    int n,k,top,ans[N],tot,rt[N];
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void Modify(re int &x,re int l,re int r,re int p,re int w)
    {
      if(!x) x=++tot;t[x].sum+=w;
      if(l==r) return;
      re int mid=l+r>>1;
      if(p<=mid) Modify(t[x].ls,l,mid,p,w);else Modify(t[x].rs,mid+1,r,p,w);
    }
    il int Query(re int x,re int l,re int r,re int ql,re int qr)
    {
      if(!x||(ql<=l&&r<=qr)) return t[x].sum;
      re int mid=l+r>>1,res=0;
      if(ql<=mid) res=Query(t[x].ls,l,mid,ql,qr);
      if(qr>mid) res+=Query(t[x].rs,mid+1,r,ql,qr);
      return res;
    }
    int main()
    {
      n=gi();k=gi();
      fp(i,1,n) q[i]=(node){gi(),gi(),gi(),1};
      sort(q+1,q+1+n);
      fp(i,1,n)
        if(q[i]==a[top]) a[top].s++;
          else a[++top]=q[i];
      fp(i,1,top)
        {
          for(re int j=a[i].b;j;j-=j&-j) a[i].ans+=Query(rt[j],1,k,1,a[i].c);
          ans[a[i].ans+a[i].s-1]+=a[i].s;
          for(re int j=a[i].b;j<=k;j+=j&-j) Modify(rt[j],1,k,a[i].c,a[i].s);
        }
      fp(i,0,n-1) printf("%d
    ",ans[i]);
      return 0;
    }
    

    T2

    CJOJ2616 - 【HZOI 2016】偏序 I
    要求四维偏序???
    第一维排序,第二维CDQ,第三维CDQ,第四维值域树状数组
    要实现CDQ的嵌套,只需在弄前几维CDQ时打个标记,标明它在这次CDQ中是在左边还是在右边处理的。往后到最后一维时,只有标记全为左才能修改,只有标记全为右才能统计进答案。
    还有,前几维CDQ只用打标记和归并、分治就成了(所以几乎一模一样,可以直接蒯、再改一改q后的数字),什么修改、统计答案都是最后一维的事。
    板子放下面

    T3

    CJOJ2375 - 【HZOI 2015】偏序 II
    要求五维偏序???
    第一维排序,第二到四维CDQ,第五维值域树状数组

    il void CDQ3(re int L,re int R)
    {
        if(L==R) return;
        re int mid=L+R>>1;
        CDQ3(L,mid);CDQ3(mid+1,R);
        re int top=L,l=L,r=mid+1;
        while(l<=mid&&r<=R)
        {
            if(q2[l].d<q2[r].d) //......
            {
                if(!q2[l].tag&&!q2[l].tag1) add(q2[l].e);
                q3[top++]=q2[l++];
            }
            else 
            {
                if(q2[r].tag&&q2[r].tag1) ans+=Query(q2[r].e);
                q3[top++]=q2[r++];
            }
        }
        while(l<=mid) q3[top++]=q2[l++];//一一对应关系的结束,标志着左边再不能对右边造成影响
        while(r<=R) 
        {
            if(q2[r].tag&&q2[r].tag1) ans+=Query(q2[r].e);//...
            q3[top++]=q2[r++];
        }
        fp(i,L,R) 
        {
            if(!q2[i].tag&&!q2[i].tag1) Clear(q2[i].e);
            q2[i]=q3[i];
        }
    }
    il void CDQ2(re int L,re int R)
    {
        if(L==R) return;
        re int mid=L+R>>1;
        CDQ2(L,mid);CDQ2(mid+1,R);
        re int top=L,l=L,r=mid+1;
        while(l<=mid&&r<=R)
        {
            if(q1[l].c<q1[r].c) q1[l].tag1=0,q2[top++]=q1[l++];
            else q1[r].tag1=1,q2[top++]=q1[r++];
        }
        while(l<=mid) q1[l].tag1=0,q2[top++]=q1[l++];
        while(r<=R) q1[r].tag1=1,q2[top++]=q1[r++];
        fp(i,L,R) q1[i]=q2[i];
        CDQ3(L,R);
    }
    il void CDQ1(re int L,re int R)
    {
        if(L==R) return;
        re int mid=L+R>>1;
        CDQ1(L,mid);CDQ1(mid+1,R);
        re int top=L,l=L,r=mid+1;
        while(l<=mid&&r<=R)
        {
            if(q[l].b<q[r].b) q[l].tag=0,q1[top++]=q[l++];
            else q[r].tag=1,q1[top++]=q[r++];
        }
        while(l<=mid) q[l].tag=0,q1[top++]=q[l++];
        while(r<=R) q[r].tag=1,q1[top++]=q[r++];//归并排序???
        fp(i,L,R) q[i]=q1[i];
        CDQ2(L,R);
    }
    

    然后因常数过大而被其它做法暴踩

  • 相关阅读:
    js-jquery-003-条形码-二维码【QR码】
    js-jquery-002-条形码-一维码
    js-jquery-001-条形码概述
    java-mybaits-00401-Mapper-输入输出
    tools-eclipse-002-常用插件
    java-mybaits-00301-SqlMapConfig
    java-mybaits-00203-DAO-mapper代理开发方法,多参数【推荐】
    java-mybaits-00202-DAO-原始DAO开发方法
    java-mybaits-00201-DAO-SqlSession使用范围
    java-mybaits-00103-入门程序原生的【查、增、删、改】
  • 原文地址:https://www.cnblogs.com/yanshannan/p/8714342.html
Copyright © 2020-2023  润新知