CDQ分治小结

warning:此文仅用博主复习使用,初学者看的话后果自负。。

复习的时候才发现以前根本就没写过这种东西的总结,简单的扯一扯

cdq分治的经典应用就是解决偏序问题

比如最经典的三维偏序问题

给出\(n\)个数,每个数\(i\),有三个属性\(a_i, b_i, c_i\),现在我们要统计对于每个\(i\),\(a_j \leqslant a_i, b_j \leqslant b_i, c_j \leqslant c_i\)的个数

显然我们可以先把所有数都按\(a_i\)排序一遍,这样考虑每个位置\(i\)的时候只需要考虑它前面的贡献即可

接下来我们递归处理区间\([1, N]\)。

设分治中心为\(mid\),cdq分治的主要思想递归处理每一段区间,只考虑过分治中心的贡献。

同时,我们采用归并排序的思想,保证每一次统计答案的时候区间\([l, mid]\)和\([mid +1, r]\)内的元素的\(b_i\)都是相对有序的

这样我们只需要用两个指针扫一遍,同时用树状数组来维护一下\(c_i\)即可

好像说的挺抽象的,貌似直接看代码会好很多?

void CDQ(int l, int r) {
if(l >= r) return ;//区间不合法
int mid = l + r >> 1;
CDQ(l, mid); CDQ(mid + 1, r);//递归下去处理子区间,处理完之后保证区间内的bi相对有序
int nl = l, nr = mid + 1, top = l - 1, sum = 0;//使用两个指针来归并本区间
while(nl <= mid || nr <= r) {//st数组记录的时把两端区间按bi大小合并后的值
if((nr > r) || (nl <= mid && A[nl].b <= A[nr].b)) T.add(A[nl].c, A[nl].w), st[++top] = A[nl++];//用树状数组维护ci的贡献
else A[nr].id += T.Query(A[nr].c ), st[++top] = A[nr++];//直接查询即可
}
for(int i = l; i <= mid; i++) T.add(A[i].c, -A[i].w);//把左边区间的影响消除
for(int i = l; i <= r; i++) A[i] = st[i];//按bi排序
}

然而有一种非常恶心的情况:即\(a_i = a_j, b_i = b_j, c_i = c_j\)

他们内部的贡献往往是不好考虑的,一个最直观的想法是直接把这些相同的数看成一个,统计答案的时候直接加上他们的数量即可

模板

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

#include<bits/stdc++.h>
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<22, stdin), p1 == p2) ? EOF : *p1++)
using namespace std;
const int MAXN = 2e5 + 10;
char buf[(1 << 22)], *p1 = buf, *p2 = buf;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
char obuf[1<<24], *O=obuf;
void print(int x) {
if(x > 9) print(x / 10);
*O++= x % 10 + '0';
}
int N, ans[MAXN];
struct Array {
int a, b, c, id, w;
bool operator == (const Array &rhs) const {
return a == rhs.a && b == rhs.b && c == rhs.c;
}
bool operator < (const Array &rhs) const {
return(a == rhs.a ? (b == rhs.b ? c < rhs.c : b < rhs.b) : a < rhs.a);
}
}A[MAXN], st[MAXN];
struct Node {
#define lb(x) (x & (-x))
int T[MAXN], Lim;
void add(int x, int v) {
while(x <= Lim) T[x] += v, x += lb(x);
}
int Query(int x) {
int ans = 0;
while(x) ans += T[x], x -= lb(x);
return ans;
}
}T;
void CDQ(int l, int r) {
if(l >= r) return ;
int mid = l + r >> 1;
CDQ(l, mid); CDQ(mid + 1, r);
int nl = l, nr = mid + 1, top = l - 1, sum = 0;
while(nl <= mid || nr <= r) {
if((nr > r) || (nl <= mid && A[nl].b <= A[nr].b)) T.add(A[nl].c, A[nl].w), st[++top] = A[nl++];
else A[nr].id += T.Query(A[nr].c ), st[++top] = A[nr++];
}
for(int i = l; i <= mid; i++) T.add(A[i].c, -A[i].w);
for(int i = l; i <= r; i++) A[i] = st[i];
}
int main() {
N = read(); T.Lim = read();
for(int i = 1; i <= N; i++) A[i].a = read(), A[i].b = read(), A[i].c = read(), A[i].w = 1;
stable_sort(A + 1, A + N + 1);
int num = 1;
for(int i = 2; i <= N; i++){
if(A[i] == A[num]) A[num].w++;
else A[++num] = A[i];
}
CDQ(1, num);
for(int i = 1; i <= num; i++) ans[A[i].id + A[i].w - 1] += A[i].w;
for(int i = 0; i < N; i++) print(ans[i]), *O++ = '\n';
fwrite(obuf, O-obuf, 1 , stdout);
return 0;
}

CDQ分治小结的更多相关文章

  1. CDQ分治总结

    \(CDQ\)分治小结 标签:知识点总结 阅读体验:https://zybuluo.com/Junlier/note/1326395 身为一个资深菜鸡 \(CDQ\)分治一开始学了一晚上,后来某一天又 ...

  2. CDQ分治&整体二分学习个人小结

    目录 小结 CDQ分治 二维LIS 第一道裸题 bzoj1176 Mokia bzoj3262 陌上花开 bzoj 1790 矩形藏宝地 hdu5126四维偏序 P3157 [CQOI2011]动态逆 ...

  3. CDQ分治题目小结

    CDQ分治属于比较特殊的一类分治,许多问题转化为这类分治的时候,时空方面都会有很大节省,而且写起来没有这么麻烦. 这类分治的特殊性在于分治的左右两部分的合并,作用两部分在合并的时候作用是不同的,比如, ...

  4. CDQ分治与整体二分小结

    前言 这是一波强行总结. 下面是一波瞎比比. 这几天做了几道CDQ/整体二分,感觉自己做题速度好慢啊. 很多很显然的东西都看不出来 分治分不出来 打不出来 调不对 上午下午晚上的效率完全不一样啊. 完 ...

  5. cdq分治的小结

    cdq分治 是一种特殊的分治 他的思想: 1.分治l,mid 2.分治mid+1,r 3.计算l,mid对mid+1,r的影响 3就是最关键的地方 这也是cdq的关键点 想到了这一步基本就可以做了 接 ...

  6. 【教程】简易CDQ分治教程&学习笔记

    前言 辣鸡蒟蒻__stdcall终于会CDQ分治啦!       CDQ分治是我们处理各类问题的重要武器.它的优势在于可以顶替复杂的高级数据结构,而且常数比较小:缺点在于必须离线操作. CDQ分治的基 ...

  7. BZOJ 2683 简单题 ——CDQ分治

    [题目分析] 感觉CDQ分治和整体二分有着很本质的区别. 为什么还有许多人把他们放在一起,也许是因为代码很像吧. CDQ分治最重要的是加入了时间对答案的影响,x,y,t三个条件. 排序解决了x ,分治 ...

  8. HDU5618 & CDQ分治

    Description: 三维数点 Solution: 第一道cdq分治...感觉还是很显然的虽然题目不能再傻逼了... Code: /*=============================== ...

  9. 初识CDQ分治

    [BZOJ 1176:单点修改,查询子矩阵和]: 1176: [Balkan2007]Mokia Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 200 ...

随机推荐

  1. Android开发 - 解决DialogFragment在全屏时View被状态栏遮住的问题

    我的上一篇文章:设置DialogFragment全屏显示 可以设置对话框的内容全屏显示,但是存在在某些机型上顶部的View被状态栏遮住的问题.经过测试,发现了一种解决办法,在DialogFragmen ...

  2. Linux pwn入门教程(6)——格式化字符串漏洞

    作者:Tangerine@SAINTSEC 0x00 printf函数中的漏洞 printf函数族是一个在C编程中比较常用的函数族.通常来说,我们会使用printf([格式化字符串],参数)的形式来进 ...

  3. OpenStack-Ocata版+CentOS7.6 云平台环境搭建 — 5.在控制节点上部署计算服务Nova

    计算服务Nova使用OpenStack Compute来托管和管理云计算系统. OpenStack Compute是基础架构即服务(IaaS)系统的主要部分. 主要模块用Python实现.OpenSt ...

  4. Python基础教程(第3版) 笔记(一)

    1.1 数和表达式: 除法运算的结果为小数,即浮点数 >>>1/2 0.5 除法运算为整数,使用双斜杠 >>>1//2 0 >>>5.0//2.4 ...

  5. linux中awk的使用

    在linux中awk绝对是核心工具,特别是在查找搜索这一领域,和掌握sed命令一样重要 下面为awk的一些基本知识,基于这些知识,可以让你随意操控一个文件: 在awk中:()括号为条件块,{}为执行的 ...

  6. 13-02 Java 数组高级算法,Arrays类

    冒泡排序 冒泡排序原理 冒泡排序代码: package cn.itcast_01; /* * 数组排序之冒泡排序: * 相邻元素两两比较,大的往后放,第一次完毕,最大值出现在了最大索引处 */ pub ...

  7. keras中的mini-batch gradient descent (转)

    深度学习的优化算法,说白了就是梯度下降.每次的参数更新有两种方式. 一. 第一种,遍历全部数据集算一次损失函数,然后算函数对各个参数的梯度,更新梯度.这种方法每更新一次参数都要把数据集里的所有样本都看 ...

  8. Linux编程 4 (创建文件touch,复制文件cp,tab补全,链接文件ln)

    一.创建文件 使用touch 可以创建空文件,例如opt目录下创建test2.txt文件.这种一般是有些程序必须要先创建文件,才能使用. 二. 复制文件 2.1 使用cp命令来复制文件,需要两个参数- ...

  9. Spring Boot + Spring Cloud 构建微服务系统(八):分布式链路追踪(Sleuth、Zipkin)

    技术背景 在微服务架构中,随着业务发展,系统拆分导致系统调用链路愈发复杂,一个看似简单的前端请求可能最终需要调用很多次后端服务才能完成,那么当整个请求出现问题时,我们很难得知到底是哪个服务出了问题导致 ...

  10. 讲解ontouchstart、ontouchend、onclick区别和坑点

    今天要讲的这个并不复杂,我用一个例子来讲解吧 <div id="box"></div> var box = document.querySelector(& ...