作为一个永不咕咕咕的博主,我来更笔记辣qaq


CDQ分治

CDQ分治的思想还是比较简单的。它的基本流程是:

\(1.\)将所有修改操作和查询操作按照时间顺序并在一起,形成一段序列。显然,会影响查询操作结果的修改操作在序列中一定会在这一个查询操作前面

\(2.\)将这一段序列分为左右两半,递归解决左右两半的子问题

\(3.\)考虑左半部分的修改操作对右半部分的查询操作的贡献

CDQ分治的基本思想就是在分治的过程中统计左半部分对右半部分的影响

上面的过程可能比较抽象,举个栗子:归并排序求逆序对

别告诉我你只会树状数组求逆序对

回顾一下归并排序求逆序对的过程:

\(1.\)将当前待排序的序列分为左右两个区间,计算左右区间内部的逆序对数量并将左右区间分别排好序

\(2.\)通过双指针计算一个数在左区间、另一个数在右区间的逆序对数量并将原序列排好序

上面加粗的部分与CDQ分治是异曲同工的。

还有些模糊其实是很正常的,下面来看几道例题感受一下:

三维偏序

二维偏序就是逆序对统计,那么三维偏序应该怎么做呢?我们设三个维度为\((a,b,c)\),那么方法如下:

\(1.\)对所有点的\(a\)维排序

\(2.\)对\(b\)维归并排序

然后我们考虑在分治过程中如何统计左半边区间对右半边区间的贡献。

可以发现,在当前分治区间合并\(b\)维的时候,\(a\)维是无序的,但是一定会有左半区间的点的\(a\)小于右半区间的点的\(a\),所以我们不需要考虑\(a\),只需要统计对于每一个右半区间的点,有多少个\(b,c\)维同时小于它即可。

这个可以在对\(b\)维进行归并排序的时候使用树状数组对\(c\)维进行维护。

然后就做完了QuQ复杂度\(O(nlog^2n)\)

注意一些小细节:

①在合并左右两个区间时可以直接快速排序,因为分治+树状数组已经有\(log^2n\)的复杂度了,快排不会影响最后的复杂度,而且似乎比直接归并要快一些

②每一次树状数组清空的时候不要暴力\(memset\)(这样子复杂度就会退化为\(O(n^2)\)),应该是将左半区间的贡献在树状数组中清除,这样才能保证复杂度。

当然了以上的方法是可以拓展到多维的(使用CDQ+CDQ+CDQ+...+树状数组解决),但是一般当维度大于\(4\)维时使用\(bitset\)会有更优的复杂度。

这里是代码菌

动态逆序对

考虑每一次删除实际上会减掉什么

首先考虑会减掉这一个数组成的逆序对数量,然后能够发现会减多一部分。

减多了什么呢?如果两个数能够产生逆序对,那么在这两个数都被删除的时候,这一个逆序对会被算两次。

那么我们考虑在后面被删除的数被删除的时候将这一个贡献加回来

对于每一次删除操作,设其为\((time,pos,val)\),那么我们需要对于每一个\(j\)求满足\(time_i<time_j,pos_i>pos_j,val_i<val_j\)或者\(time_i<time_j,pos_i<pos_j,val_i>val_j\)的\(i\)的数量。可以发现这是一个三维偏序,然后直接上板子即可。

代码

NOI2007 货币兑换

这一道题是CDQ论文里面的题目

做这道题你需要的前置芝士是斜率优化,顺便给我的斜率优化笔记打个广告QAQ

我们来愉快地推\(DP\)

设\(f_i\)表示在第\(i\)天能够获得的最大收益,设\(g_i\)为第\(i\)天最多能够兑换的\(B\)券数量

那么有\(g_i \times B_i + g_i \times Rate_i \times A_i = f_i\),即\(g_i = \frac{f_i}{B_i + Rate_i \times A_i}\)

而我们的\(DP\)转移为:\(f_i = \max\{A_i\max\limits_{j=1}^{i-1}\{g_j\frac{B_i}{A_i} + g_jRate_j\},f_{i-1}\}\)

可以发现我们的转移是一个斜率式子,可以使用斜率优化解决。但是\(g_i\)与\(i\)并不呈正相关,意味着我们不能像传统的斜率优化一样使用单调队列解决凸包的维护问题

这个时候有两种做法:使用平衡树维护凸包和使用分治维护凸包。第一种不是这篇文章的重点,所以我们直接看第二种

考虑当前正在求\([l,r]\)的\(f\)值,首先我们先递归进入左区间,将左区间的\(f\)和\(g\)的值求出来,然后先在当前层处理左区间对右区间的影响,然后递归进入右区间求解

对于当前区间的贡献的计算,首先将左区间的所有直线按照斜率排序,使用单调栈建立凸包,然后将右区间的所有询问按照\(x\)递增排序,因为\(x\)递增,所以可以直接在凸包上扫一遍得到答案。当然这一部分也可以不排序,通过在凸包上二分解决。

注意到上面的递归顺序与我们之前见到的大部分分治的递归顺序是不同的,这是因为:如果我们需要正确地得到\(f_i\),意味着对于所有的\(j<i\),\(f_j\)必须要在\(f_i\)之前算好并将贡献给\(f_i\),\(f_i\)才能够正确地给其他值进行贡献。如果我们在当前区间计算贡献之前计算右区间,意味着右区间的所有\(f_i\)都在没有正确地计算出值得情况下对其他值进行了贡献,这会导致答案的错误。

代码

练习题:BZOJ4237 稻草人

题解在这里

整体二分

整体二分与CDQ分治的做法有些像

整体二分的基本思路是将一堆操作和询问放在一起进行二分,一般可以做到\(O(nlog^2n)\)的复杂度

假设我们现在正在解决\(solve(ql,qr,l,r)\),这表示我们正在解决\([ql,qr]\)区间内的操作和询问,而且满足:区间内所有修改操作的权值范围在\([l,r]\)内,并且已经确定区间内所有询问的答案在\([l,r]\)区间内

接下来是整体二分最重要的\(check\)部分:

取\(mid=\frac{l+r}{2}\),从左往右执行所有修改和查询操作:

如果某个修改操作的权值范围在\([l,mid]\)内,执行它,否则忽略掉它

如果到了一个查询操作,进行查询,如果满足答案在\([l,mid]\)内的条件,那么它的答案就在\([l,mid]\)范围内,否则在\([mid+1,r]\)范围内

最后把所有权值范围在\([l,mid]\)的修改操作和答案在\([l,mid]\)的查询放在一起,其他的修改和查询放在一起,两边分别递归求解。递归边界为\(l=r\),就可以确定\([ql,qr]\)的所有询问的答案为\(l\)。

当然,可能在实际的\(check\)过程中需要执行的是\([mid+1,r]\)部分的修改操作。

可能理解起来比较抽象(分治理解起来都比较抽象)来看两道题感受一下:

ZJOI2010 K大数查询

这道题曾经树套树时空双卡,但整体二分表示很轻松;现在空间开大了,但是树套树还是因为超大的常数容易被卡……

考虑\(solve\)函数的\(check\)过程,不妨这么做:

使用一个权值线段树维护每一个位置中,数字范围在\([mid+1,r]\)的数的个数。

对于在\([mid+1,r]\)范围内的修改,也就是加入的数在\([mid+1,r]\)范围内的操作,在它对应的操作区间上\(+1\);

对于一个查询操作,如果这个查询操作对应的区间上数字范围在\([mid+1,r]\)的数的个数大于等于这一次询问的\(K\),则表示这一个询问的答案在\([mid+1,r]\)范围内,否则在\([l,mid]\)范围内。

值得注意的是,要清除\([mid+1,r]\)的修改操作对答案在\([l,mid]\)范围内的询问的贡献。

代码

HNOI2016 网络

这道题数据比较水,以至于树链剖分+线段树+堆的\(O(nlog^3n)\)算法都怼过去了

仍然考虑\(check\)函数,我们只对权值在\([mid+1,r]\)的范围内的修改进行操作,操作的方式考虑差分:预处理树的\(dfs\)序,对于一个加入操作\(a,b\),在\(dfn_a,dfn_b\)处\(+1\),然后在\(dfn_{LCA(a,b)},dfn_{fa_{LCA(a,b)}}\)处\(-1\),删除操作反过来即可。

对于一个查询操作,我们需要查询是否当前加入的所有链都经过了当前点,我们直接查询当前点对应的子树中的权值和,如果它与当前加入的链数相等就表示所有链都经过了,答案在\([l,mid]\)范围内,否则在\([mid+1,r]\)范围内。正确性显然分类讨论一下就行了

代码来这里看啦啦啦

练习题:POI2011 Meteors

题解在这里

总结

CDQ分治与整体二分是两种比较优秀的分治算法,一般适用于以下情形:

\(1.\)不强制在线

\(2.\)对时间限制和空间限制要求比较高,使用高级数据结构(点名树套树!)容易被卡

虽然离线是这两种分治算法最大的软肋,出题人只需要加上异或\(lastans\)分治就GG了,但仍然不能改变这两种分治算法的重要地位。

一篇自己都看不懂的CDQ分治&整体二分学习笔记的更多相关文章

  1. Cdq分治整体二分学习记录

    这点东西前前后后拖了好几个星期才学会……还是自己太菜啊. Cdq分治的思想是:把问题序列分割成左右两个,先单独处理左边,再处理左边对右边的影响,再单独处理右边.这样可以消去数据结构上的一个log,降低 ...

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

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

  3. 一篇自己都看不懂的点分治&点分树学习笔记

    淀粉质点分治可真是个好东西 Part A.点分治 众所周知,树上分治算法有$3$种:点分治.边分治.链分治(最后一个似乎就是树链剖分),它们名字的不同是由于分治方式的不同的.点分治,顾名思义,每一次选 ...

  4. 一篇自己都看不懂的Matrix tree总结

    Matrix tree定理用于连通图生成树计数,由于博主太菜看不懂定理证明,所以本篇博客不提供\(Matrix\ tree\)定理的证明内容(反正这个东西背结论就可以了是吧) 理解\(Matrix\ ...

  5. [学习笔记] CDQ分治&整体二分

    突然诈尸.png 这两个东西好像都是离线骗分大法... 不过其实这两个东西并不是一样的... 虽然代码长得比较像 CDQ分治 基本思想 其实CDQ分治的基本思想挺简单的... 大概思路就是长这样的: ...

  6. 算法笔记--CDQ分治 && 整体二分

    参考:https://www.luogu.org/blog/Owencodeisking/post-xue-xi-bi-ji-cdq-fen-zhi-hu-zheng-ti-er-fen 前置技能:树 ...

  7. CDQ分治与整体二分学习笔记

     CDQ分治部分 CDQ分治是用分治的方法解决一系列类似偏序问题的分治方法,一般可以用KD-tree.树套树或权值线段树代替. 三维偏序,是一种类似LIS的东西,但是LIS的关键字只有两个,数组下标和 ...

  8. 对于挑战书上的很久之前都看不懂的DP看懂的突破

    突破一..牢记问题概念 并且牢记dp状态方程 突破二..一直有一个求和dp转化成O1dp递推的式子看不懂.. 看不懂的原因是..没有分清求和符号作用的范围 提醒:以后遇到求和符号一定明确其求和的式子的 ...

  9. 现在连Linux都搞不懂,当初我要是这么学习操作系统就好了!

    原创声明 本文首发于微信公众号[程序员黄小斜] 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 本文思维导图 简介 学习编程,操作系统是你必须要掌握的基础知识,那么操作系统到底是什么呢? 这还 ...

随机推荐

  1. 第三次web作业

    em是相对长度单位.相对于当前对象内文本的字体尺寸.如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸,最初是指字母M的宽度,故名em.现指的是字符宽度的倍数,用法类似百分比,如:0 ...

  2. 原生js实现二级联动下拉列表菜单

    二级联动下拉列表菜单的难点在于对后台返回的数据进行解析,不多逼逼,直接上代码 上图是后台返回的数据 实现代码如下: var deviceNotExist = true;//防止数据重复 if(data ...

  3. recovery 差分升级包制作超时

    我们在对android系统升级的时候,可以减少升级包的大小,只升级差异部分,也就是差分包升级,相关的描述可以参考:http://blog.csdn.net/csdn66_2016/article/de ...

  4. 洗礼灵魂,修炼python(55)--爬虫篇—知识补充—RFC 2616 http状态码

    不多说直接上状态码表: 状态码 含义 100 客户端应当继续发送请求.这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝.客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽 ...

  5. 辽宁移动宽带体验及魔百盒M101s-2刷机

    一.背景 坐标:辽宁 某城,移动宽带100M. 设备:移动赠送,华为光猫一只,魔百盒M101s-2电视盒子 一只,据安装人员说这个魔百盒是移动自己开发设计的. 二.上网体验 上网:浏览一般网站没问题. ...

  6. [转]extern "C"的作用

    extern "C"的主要作用就是为了能够正确实现C++代码调用其它C语言代码. 加上extern “C”后,会指示编译器将这部分代码按C语言进行编译,而不是C++的.这是因为C+ ...

  7. strong vs copy

    一.前言           在这里,我通过实例去介绍strong和copy的区别(%p打印出来对象的地址)  ViewController.h            #import <UIKi ...

  8. Python 使用 xlwings 往 excel 中写入一行数据的两种方法

    该方法跟上一篇写入一列的方法相反,代码如下: # -*- coding:utf-8 -*- import xlwings as xw list1 = [1,2,3,4,5] list2 = [[1], ...

  9. <20190103>别傻了,一些 新的技术注定只适合新人

    故障现象:    用vmware跑的虚拟机不兼容 某银行网银的U盾. 插入usb口后无法识别U盾, 解决过程: 1  更换2台各自不同电脑, 使用前置USB口, 后置USB口.  故障依旧. 2  使 ...

  10. centos7下安装docker(3.1创建镜像commit)

    docker commit创建镜像 步骤:1.运行容器 2.修改容器 3.将容器保存为镜像 1. 注:-it是以交互模式进入容器,并打开终端 2.安装一个vim进行修改镜像 yum install - ...