二维数据结构->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. python放弃篇(Django/爬虫)

    第一篇:Django系列 第二篇:爬虫系列 待续……

  2. 开放源代码的设计层面框架Spring——day03

    spring第三天     一.AOP的相关概念         1.1AOP概述             1.1.1什么是AOP                 AOP:全称是Aspext Orie ...

  3. python3 练手实例3 摄氏温度与华氏温度转换

    def wd(): w=input('请输入一个摄氏温度或者一个华氏温度,如,34c/C or 34f/F:') if w[-1] in ['c','C']: w=float(w[:-1]) hs=1 ...

  4. 2018-2019-2 20165234 《网络对抗技术》 Exp2 后门原理与实践

    实验二 后门原理与实践 实验内容 (1)使用netcat获取主机操作Shell,cron启动 (2)使用socat获取主机操作Shell, 任务计划启动 (3)使用MSF meterpreter(或其 ...

  5. 第十二节,OpenCV学习(一)图像的读取、显示、保存

    一.读取图像 所谓的图像就是一个数组,对图像的处理就是对数字的处理 import cv2 import numpy as np img = cv2.imread('dog.jpg') print(im ...

  6. Depp Learning note Day1

    1.softmax函数的优化来防止溢出 2.np.argmax()函数的使用 返回值为数组中最大值的索引 a = [1, 2, 3, 4, 3, 7] print(np.argmax(a)) 若np. ...

  7. Laravel-google-authenticator--Google验证码

    开发前的准备 安装Laravel 安装二维码生成器QrCode,没有安装也可以,接下来会安装 安装拓展 1.运行如下代码安装拓展包: composer require "earnp/lara ...

  8. SQLAlchemy+Flask-RESTful使用(三)

    前言 顺理成章地,19.3.21起笔了第三章.也就是最近没啥事了,才有时间搞这些.生命不息奋斗不止吧! 变更记录 # 19.3.21 起笔 # 19.3.21 增加 Flask-RESTful如何获取 ...

  9. 【JS】VUE学习

    VUE的全家桶:vue-cli,vue-router,vue-resource,vuex 环境搭建:https://www.jianshu.com/p/32beaca25c0d 先码在这儿吧. htt ...

  10. curl常用命令备忘

    #####(输出请求头信息) curl -I xxx-Pro:test xxx$ curl -I https://www.baidu.com/ HTTP/1.1 200 OK Accept-Range ...