• 【模板】CDQ分治


    其实我的CDQ分治写的和shi一样

    参悟了好长时间才大概知道CDQ分治该怎么搞,按照网上的资料半抄半写弄了道BZOJ3262陌上花开,但是评测不了,只把样例给过了,所以仍然不知道这个板子是不是对的。

    以下叙述都是博主从其他BLOG里东拼西凑的:

    CDQ分治用来解决一类可离线的问题,通常是有一堆奇奇怪怪的修改和询问,然后拿高级数据结构做来很恶心的题目。

    CDQ分治的基本套路:

    1、把待处理区间[l,r]分为[l,mid]和[mid+1,r]两个区间,递归处理下去。

    2、处理[l,mid]区间的修改对[mid+1,r]区间的影响

    很明显难点在于2,这个因题而异。

    CDQ分治的一个基本应用就是求解三维偏序有关的问题,而很多CDQ分治的题目也可以转化成三维偏序来做,有的带修改查询的题目会把时间也算一维。

    拿陌上花开来说下拿CDQ分治做三维偏序的套路

    0、先按a从小到大排序,形成一个序列。

    1、把待处理区间[l,r]分为[l,mid]和[mid+1,r]两个区间,递归处理下去。

      1.1、把左右两个区间分别按b从小到大排序(注意到这时左区间的x值一定小于右区间的x值)

      1.2、对于右区间的每一个b,把左区间的每一个b<=b的z值加入到一个树状数组里,并查询z<=z的个数并计入答案(由于b值已经排序,只要两个指针扫一下就好了)

      1.3、把树状数组恢复原样

    说的不是很清楚,贴一下 BZOJ3262 陌上花开 的代码当作板子

     1 #include<stdio.h>
     2 #include<algorithm>
     3 using namespace std;
     4 struct eve
     5 {
     6     int a,b,c,cont,ans;
     7     bool operator == (const eve y) const
     8     {return a==y.a&&b==y.b&&c==y.c;}
     9 };
    10 eve A[100005],B[100005];
    11 int tot,n,k,cont[200005],ans[100005];
    12 void add(int p,int q){for(p;p<=k;p+=(p&(-p)))cont[p]+=q;}
    13 int sum(int p)
    14 {
    15     int ans = 0;
    16     for(p;p;p-=(p&(-p))) ans += cont[p];
    17     return ans; 
    18 }
    19 bool comp1(const eve &a,const eve &b)
    20 {
    21     if(a.a==b.a) return a.b==b.b ? a.c<b.c : a.b<b.b;
    22     else return a.a<b.a;
    23 }
    24 bool comp2(const eve &a,const eve &b)
    25 {return a.b==b.b ? a.c<b.c : a.b<b.b;}
    26 void cdq(int,int);
    27 int main()
    28 {
    29     scanf("%d%d",&n,&k);
    30     int i,j;
    31     for(i=1;i<=n;i++)
    32         scanf("%d%d%d",&A[i].a,&A[i].b,&A[i].c);
    33     sort(A+1,A+n+1,comp1);
    34     for(i=1;i<=n;i=j)
    35     {
    36         B[++tot] = A[i];
    37         for(j=i+1;j<=n&&A[j]==A[i];j++);
    38         B[tot].cont = j-i; 
    39     }
    40     cdq(1,tot);
    41     for(i=1;i<=tot;i++) ans[B[i].ans+B[i].cont-1] += B[i].cont;
    42     for(i=0;i<n;i++) printf("%d
    ",ans[i]);
    43     return 0;
    44 }
    45 void cdq(int l,int r)
    46 {
    47     if(l==r) return;
    48     int mid = (l+r)>>1;
    49     cdq(l,mid); cdq(mid+1,r);
    50     sort(B+l,B+mid+1,comp2); sort(B+mid+1,B+r+1,comp2);
    51     int i=l,j;
    52     for(j=mid+1;j<=r;j++)
    53     {
    54         for(;i<=mid&&B[i].b<=B[j].b;i++)
    55         {add(B[i].c,B[i].cont);}
    56         B[j].ans += sum(B[j].c);
    57     }
    58     for(j=l;j<i;j++) add(B[j].c,-B[j].cont);
    59 } 
    View Code

    然而CDQ分治的提出是在这篇论文里:《从<Cash>谈一类分治算法的应用》 陈丹琦 这篇论文里提到的两道题目似乎并不如陌上花开容易理解果然是我太弱了。

    其实写的6的板子不会多加什么解释,只有这种写的跟SB似的板子才会在这里瞎BB。

  • 相关阅读:
    HDU-1875-畅通工程再续(最小生成树)
    HDU-1325-Is It A Tree?(并查集+有向树)
    HDU-2473-Junk-Mail Filter(并查集删除)
    HDU-1233-还是畅通工程(最小生成树)
    POJ-2492-A Bug's Life(并查集分类)
    asp.net core 中KindEditor的使用
    使用FormsAuthenticationTicket进行登陆验证
    c#生成随机字符串
    string.Format对C#字符串格式化
    在Ubuntu上使用noip动态域名的方法(ddns)
  • 原文地址:https://www.cnblogs.com/hzs2000/p/6714053.html
Copyright © 2020-2023  润新知