【模板】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\n",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 } 

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

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

时间: 2024-10-25 00:25:30

【模板】CDQ分治的相关文章

P3810 【模板】三维偏序(陌上花开)cdq分治

传送门:https://www.luogu.org/problemnew/show/P3810 cdq分治的模板题,第一层外部排序,第二层cdq归并排序,这个时候不用考虑第一次的顺序,第三次用树状数组. 注意,不要用memset,用队列保存加上的值,最后在把加上的值减去就行了. #include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include &l

洛谷P3374 【模板】树状数组 1(CDQ分治)

题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3个整数,表示一个操作,具体如下: 操作1: 格式:1 x k 含义:将第x个数加上k 操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和 输出格式: 输出包含若干行整数,即为所有操作2的结果.

CDQ 分治算法模板

CDQ分治 1.三维偏序问题:三维偏序(陌上花开) #include<bits/stdc++.h> #define RG register #define IL inline #define _ 200005 using namespace std; IL int gi(){ RG int data = 0 , m = 1; RG char ch = 0; while(ch != '-' && (ch<'0'||ch>'9'))ch = getchar(); if(

洛谷 P3810 【模板】三维偏序(陌上花开) (cdq分治模板)

在solve(L,R)中,需要先分治solve两个子区间,再计算左边区间修改对右边区间询问的贡献. 注意,计算额外的贡献时,两子区间各自内部的顺序变得不再重要(不管怎么样左边区间的都发生在右边之前),于是就少了一维 https://www.lydsy.com/JudgeOnline/problem.php?id=3262 https://www.luogu.org/problemnew/show/P3810 此题每个操作既是修改又是查询 对于此题,先按一维排序,在solve(L,R)中先solv

P3810 【模板】三维偏序(陌上花开)(cdq分治)

思路 看到这种偏序类的题目,而且不要求强制在线,可以立刻想到cdq分治 注意这题有一个问题,就是询问的是小于等于而不是小于,如果相等的话两个元素会相互贡献,而cdq的特点是右区间不能对左边有影响,所以要先去重,再然后就是板子 代码 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int n,maxn; namespace BIT{ int bit[200100]

HDU 5618:Jam&#39;s problem again(CDQ分治+树状数组处理三维偏序)

http://acm.hdu.edu.cn/showproblem.php?pid=5618 题意:-- 思路:和NEUOJ那题一样的.重新写了遍理解了一下,算作处理三维偏序的模板了. 1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 #include <cstring> 5 using namespace std; 6 #define INF 0x3f3f3f3f 7 #d

BZOJ 1176 Balkan 2007 Mokia CDQ分治

题目大意:有一些操作,给一个坐标代表的点加上一个数,和求出一个矩形中的所有数的和. 思路:一眼题,二位树状数组水过. ... .. . 哪里不对?W<=2000000.逗我?这n^2能开下? 这个时候CDQ神牛又来帮助我们了. 这个题应该算是CDQ分治的模板题了吧,简单分析一下,其实不难. 写这个题之前建议写一下BZOJ 1935 SHOI 2007 Tree 园丁的烦恼 树状数组这个题,是本题的简化版. 按照正常的解法,我们应该建立一个二位的数据结构,然后分别维护两维的信息.如果用动态开点的线

算法复习——cdq分治

题目: Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一朵花B要美丽,当且仅当Sa>=Sb,Ca>=Cb,Ma>=Mb.显然,两朵花可能有同样的属性.需要统计出评出每个等级的花的数量. Input 第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值. 以下

2016计蒜之道复赛 百度地图的实时路况 floyd+cdq分治

链接:https://nanti.jisuanke.com/t/11217 奉上官方题解: 枚举 d(x , y , z) 中的 y,把 y 从这个图中删去,再求这时的全源最短路即可,使用 Floyd 算法来做上述过程. Floyd 算法可以是一个增量的过程,虽然第一维一般都是从 1枚举到 k但是这个枚举的顺序并不影响最后的结果. 所以如果可以预处理出对于每个点 y,只剩 y 没有在 Floyd 的第一维枚举到的矩阵,这个矩阵的值就是不经过 y 点的全源最短路. 所以使用分治,每一次把点集拆成两