二维数据结构->cdq

预备知识

偏序关系 link1

数据结构 link1, 进阶指南P212

杂烩 link1

T1: 二维树状数组

hdu1892

一维树状数组: 二进制分段思想

可以把\(m = 2^a+2^b+\cdots(a>b>\cdots)\) 分解成\((0, 2^a], (2^a , 2^a+2^b]...,(m - 2^z+1, m]\)这log个区间, 所以对于每个\(x\)存\((x-lowbit(x), x]\)

二维树状数组也是这样, 每一行的元素维护的是之前\([x-lowbit(x)+1, x]\)列该位置元素的和

所以只要在普通树状数组上再加一维就好啦

T2:cdq分治

例子: 求逆序对(这个经典问题在很多OJ上都有)

有句话叫罗马不是一天建成的, cdq分治也不是凭空想出来的,该算法与 求逆序对的两种做法有很深的关联.

逆序对的定义是 一对数\((i, j),i<j, a[i]>a[j]\) 所以我们把求逆序对问题转换为统计在x之前比x大的数有多少个这一问题 这种问题是二维偏序问题的一种(意思是要统计满足两个序的元素个数)

第一种做法是使用树状数组, 可以使用权值树状数组在\(O(logn)\)的时间内在线维护比x大的个数

第二种做法就很妙了.

我们可以发现, 如果数字排好序了, 值就很方便算(为0). 但是问题是, 我们打破了题目的一个限定(i<j).

关键是, 我们不一定需要将所有数字都排好序啊!!! 我们只需要让满足\(i<j\) 的数在数\(j\)之前出现就可以了啊!!!

所以有一个解决方法是用分治来"去掉"\(i<j\)这个"序"所带来的不方便之处. 我们可以将数分成两半, 分别统计出每一半内的答案, 之后再排好序, 求出每一个数的答案, 然后算前面的数对后面数字答案的影响. 此时, 前面一半和后面一半的所有数之间, \(i<j\)这个"序是成立的. 而在每一半数内, 答案已经求出,所有我们就可以把这一半内数字的第一维"序"(\(i<j\))打乱, 按照第二维序(\(a_i<a_j\))排序. 这样我们就可以方便的求出答案啦!!

cdq分治也是一样. 把所有的修改/询问分成两份, 统计前一半区间内的修改对后一般区间内的查询造成的影响. 这样就相当于把所有的修改放在前面,询问放在后面,所以cdq分治可以将在线算法转换成离线的.

(其实离线算法就是可以忽略时间的影响直接对x或y排序的算法)

这样, cdq分治就可以对"偏序问题"进行"降维打击", 比如, 可以把二维线段树才能做的问题变成用扫描线就可以做的问题(虽然Hold不住强制在线...呜呜呜)

强不强大?

有几个小技巧:

  1. 统计答案时用指针把每一个询问用指针映射到数组, 方便统计答案.

  2. 见归并代码

        static op tmp[siz];
    for(int pos = l, i = l, j = mid+1; pos <= r; pos++){
    if((ops[i].x <= ops[j].x && i <= mid) || j > r){
    //将前一半数放入数据结构
    if(!isquery(ops[i])) add(ops[i].y, ops[i].a);
    tmp[pos] = ops[i];
    i++;
    } else {
    if(isquery(ops[j])) *(ops[j].ans) += query(ops[j].y) * ops[j].d;
    //指针
    tmp[pos] = ops[j];
    j++;
    }
    }

    习题 : bzoj1176 mokia

bzoj1176 mokia:Debug心得

  1. 读入单个字符用cin 不用scanf
  2. 不要贸然进行一些不确定的优化

一类特殊的CDQ分治

例题:百度地图的实时路况(Jisuanke 11217)

给出伪代码 思路不写了

//处理: 如果floyd不考虑[l, r] 的话, dis数组会是怎么样
//将数组存入tmp_d[dep]中
void cdq(int l, int r, int dep){
tmp_d[dep] = t
}

附: bzoj mokia AC代码

#include<bits/stdc++.h>
using namespace std; template<typename T>
void read(T &ans){T f = 1; char c = getchar();ans = 0;while(!isdigit(c)){if(c == '-')f = -1;c = getchar();}while( isdigit(c)){ans = ans*10 + c - '0';c = getchar();}ans *= f;} const int mxm = 16*1e4, mxq = 1e4, mxw = 2e6;
struct op{
int typ;
int x, y, a, d;
int *ans;
}ops[mxm+mxq*4+1]; op getop(int x, int y, int a, int typ){
op ret; ret.x = x, ret.y = y, ret.d = 23333;ret.a = a; ret.typ = 1;return ret;
}
op getop(int x, int y, int d, int *ans, int typ){
op ret; ret.x = x, ret.y = y, ret.d = d, ret.a = 0;ret.ans = ans; ret.typ = 2;return ret;
}
bool isquery(op x){
return x.typ == 2;
}
int s, w, ans[mxq+1]; int c[mxw+5];
int query(int x){
int ret = 0;
for(; x > 0; x -= x&(-x)) ret += c[x];
return ret;
}
void add(int x, int v){
for(; x <= mxw; x += x&(-x)) c[x] += v;
}
void clr(int x){
for(; x <= mxw; x += x&(-x)) c[x] = 0;
}
void work(int l, int r){
if(l >= r) return; int mid = (l+r)>>1;
work(l, mid); work(mid+1, r); static op tmp[mxm+mxq*4+1];
for(int pos = l, i = l, j = mid+1; pos <= r; pos++){
if((ops[i].x <= ops[j].x && i <= mid) || j > r){
if(!isquery(ops[i])) add(ops[i].y, ops[i].a);
// , printf("%d %d %d ..add\n", i, ops[i].y ,ops[i].a);
tmp[pos] = ops[i];
i++;
} else {
if(isquery(ops[j])) *(ops[j].ans) += query(ops[j].y) * ops[j].d;
// , printf("%d %d %d\n", query(ops[j].y),ops[j].d, query(ops[j].y) * ops[j].d);
tmp[pos] = ops[j];
j++;
}
}
// puts("end.");
for(int i = l; i <= r; i++){
ops[i] = tmp[i];
if(!isquery(ops[i])) clr(ops[i].y); }
// for(int i = 1; i <= 10; i++)if(query(ops[i].y) != 0) printf("When l = %d, r = %d, FAAAA!!!! ON %d, get %d\n",l, r,i,(query(ops[i].y)));
}
signed main(){
// freopen("1.in", "r", stdin);
// freopen("1.out", "w", stdout);
read(s), read(w); int op = 0, cnt = 0, qcnt = 0;
int x, y, x2, y2, a;
memset(ops, 0, sizeof ops); // cout<<query(4)<<endl;
while(1){
read(op);
// cout<<op<<endl;
if(op == 3) break;
read(x), read(y); if(op == 1){
read(a);
ops[++cnt] = getop(x, y, a, 1);
} else if(op == 2){
read(x2), read(y2);
qcnt++;
// ans[qcnt] = (y2 - y + 1) * (x2 - x + 1) * s;
ops[++cnt] = getop(x2, y-1, -1, ans+qcnt, 2);
ops[++cnt] = getop(x-1, y2, -1, ans+qcnt, 2);
ops[++cnt] = getop(x-1, y-1, 1, ans+qcnt, 2);
ops[++cnt] = getop(x2, y2, 1, ans+qcnt, 2);
}
}
// for (int i = 1; i <= cnt; i++) {
// printf("%d:Triple(x = %d, y = %d, a = %d, d = %d)\n",ops[i].typ,ops[i].x, ops[i].y, ops[i].a, ops[i].d);
// }
work(1, cnt); for(int i = 1; i <= qcnt; i++) {
cout << ans[i] << endl;
}
return 0;
}
/*
0 200
1 76 150 1
1 158 170 2
1 12 4 123
2 92 59 153 141
3
*/

BZOJ1173 CDQ分治 笔记的更多相关文章

  1. CDQ分治笔记

    以前一直不会CDQ……然后经常听到dalao们说“这题直接CDQ啊”“CDQ不就秒了吗”的时候我只能瑟瑟发抖QAQ CDQ分治 其实CDQ分治就是二分分治,每次将$[l,r]$的问题划分为$[l,mi ...

  2. CDQ分治笔记+例题

    CDQ分治是一种离线分治算法,它基于时间顺序对操作序列进行分治. 看这样一个问题: 在一个三维坐标系中,有若干个点,每个点都有对应的坐标 \((X_i , Y_i , Z_i)\) ,我们要对于每个点 ...

  3. cdq分治 笔记

    算法讲解 这个算法用于解决三维偏序问题. 三维偏序:给定 \(n\) 个三元组: \((a_i,b_i,c_i)\),求同时满足满足 \(a_i\le a_j,b_i\le b_j,c_i\le c_ ...

  4. 一篇自己都看不懂的CDQ分治&整体二分学习笔记

    作为一个永不咕咕咕的博主,我来更笔记辣qaq CDQ分治 CDQ分治的思想还是比较简单的.它的基本流程是: \(1.\)将所有修改操作和查询操作按照时间顺序并在一起,形成一段序列.显然,会影响查询操作 ...

  5. 学习笔记 | CDQ分治

    目录 前言 啥是CDQ啊(它的基本思想) 例题 后记 参考博文 前言 博主太菜了 学习快一年的OI了 好像没有什么会的算法 更寒碜的是 学一样还不精一样TAT 如有什么错误请各位路过的大佬指出啊感谢! ...

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

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

  7. [学习笔记] CDQ分治 从感性理解到彻底晕菜

    最近学了一种叫做CDQ分治的东西...用于离线处理一系列操作与查询似乎跑得很快233 CDQ的名称似乎源于金牌选手陈丹琦 概述: 对于一坨操作和询问,分成两半,单独处理左半边和处理左半边对于右半边的影 ...

  8. [偏序关系与CDQ分治]【学习笔记】

    组合数学真是太棒了 $CDQ$真是太棒了(雾 参考资料: 1.<组合数学> 2.论文 课件 很容易查到 3.sro __stdcall 偏序关系 关系: 集合$X$上的关系是$X$与$X$ ...

  9. 初学cdq分治学习笔记(可能有第二次的学习笔记)

    前言骚话 本人蒟蒻,一开始看到模板题就非常的懵逼,链接,学到后面就越来越清楚了. 吐槽,cdq,超短裙分治....(尴尬) 正片开始 思想 和普通的分治,还是分而治之,但是有一点不一样的是一般的分治在 ...

随机推荐

  1. [Reinforcement Learning] 马尔可夫决策过程

    在介绍马尔可夫决策过程之前,我们先介绍下情节性任务和连续性任务以及马尔可夫性. 情节性任务 vs. 连续任务 情节性任务(Episodic Tasks),所有的任务可以被可以分解成一系列情节,可以看作 ...

  2. centos配置epel和remi源

    来源:https://blog.csdn.net/zhang197093/article/details/52057898 CentOS 内置的yum命令安装非常的简单实用,能自动帮助我们解决依赖,但 ...

  3. mysql字符集设置注意事项

    mysql字符集设置必须是在具体的某一个数据库情况下才能进行设置 否则会报错.

  4. 分布式系列十三: nginx

    nginx偏运维, 不过作为开发应该了解它能做什么事情, 其作为技术架构的一部分必不可少 正向代理和反向代理 正向代理是代理的客户端, 反向代理是代理的服务端. nginx就是一款可以作反向代理的we ...

  5. monkey日志解析

    bash arg: -p (打印monkey命令携带的参数) bash arg: com.dapp.testAPP123 bash arg: --throttle bash arg: 200 bash ...

  6. Arduino语言简介

    参考链接:https://www.cnblogs.com/xczr/p/7831343.html

  7. Git管理源代码

    Git Git 是目前世界上最先进的分布式版本控制系统(没有之一) 作用 源代码管理 为什么要进行源代码管理? 方便多人协同开发 方便版本控制 Git单人本地仓库操作 安装git sudo apt-g ...

  8. SharpMap在web上的应用

    最近公司用SharpMap做了一个做桌面程序,它是一个开源的Gis项目,功能还可以,最大的特点就是简单易用,这里介绍下怎么在web下使用: 这次我们根据demo先了解一下如何show一个地图.这是最基 ...

  9. rpmbuild analysis

  10. Java_File类

    File类以抽象的方式代表文件名和目录路径.该类主要用于文件和目录的创建.查找.删除等.先来看一下File的构造方法: // 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 File ...