A.\(CDQ\) 分治

特别基础的教程略。

\(CDQ\)分治的优缺点:
( 1 )优点:代码量少,常数极小,可以降低处理维数。
( 2 )缺点:必须离线处理。

\(CDQ\)分治与其他分治最本质的不同在于:

分治到达\([L,R]\)时,分治处理\([L,mid]\)与\([mid+1,R]\)
然后递归上来合并的时候:
只考虑 [L,mid]中元素 对 [mid+1,R] 中元素的影响

看起来这句话非常简单,但只要正真理解了这句话,也就理解了\(CDQ\)分治。
只要是满足这个原则的分治,都是可以称作\(CDQ\)分治的。
下面给出几个例子,都是为了理解这句话:

例题1:

给定长度为\(10^5\)的一个序列,有一下两种操作:
(1)type=1:x val ,把序列第x位置的元素加上val
(2)type=2:x y , 求解[x,y]元素值的和

树状数组大水题.....
如果用\(CDQ\)分治呢?(虽然非常非常没有必要).
我们把求解\([x,y]\)元素之和拆分为求前缀和\(pre[y]-pre[x-1]\)。
那么会对\(pre[x]\)产生影响的为:操作时间在其之前,修改位置\(\leq x\)的修改操作。
建立二元组\((time , x)\),外部以time排序,然后分治。
分治时,每次以\(x\)归并排序,
合并\([L,mid]\)与\([mid+1,R]\)时,统计\([L,mid]\)有几个修改操作。
那么如果在\([mid+1,R]\)碰到了修改操作,自然此时的统计数即为答案。
注意到上面:只统计[L,mid]的修改,只计算[mid+1,R]的答案。
这就是\(CDQ\)分治的关键所在,因为这样统计可以做到不重不漏,代码见这里。

例题2:

三维偏序问题:
给定\(n\)个元素,每个元素有三个属性\(a,b,c\)。
定义\(f(i) = \sum_{i=1}^n [i!=j]\times [a_j<a_i 且 b_j<b_i 且 c_j<c_i]\)
试计算\(f(1),f(2),...f(n)\),数据范围 \(n \leq 10^5\)。

三维偏序是\(CDQ\)分治的最经典应用。
先确定分治原则:
建立三元组\((a,b,c)\),外部先按照\(a\)排序,消除\(a\)的影响。
然后分治内部以\(b\)为准则进行归并。此时我们保证了\(a_j<a_i\)与\(b_j<b_i\)
第三维\(c_j<c_i\)怎么办?用以\(c\)值建立权值树状数组维护即可。
具体流程为:

(1)递归处理\([L,mid]\)与[mid+1,R];
(2)以\(b\)为准则归并,其中:
( 2>1 )若为\([L,mid]\)元素,将其\(c\)值插入权值树状数组
( 2>2 )若为\([mid+1,R]\)元素,在权值线段树中查询小于其\(c\)值的元素个数。
(3)回溯;

观察到处理时,依旧满足上面\(CDQ\)分治的特性。
如果偏序原则中为\(\leq\)即可,那么需要先去重。具体代码见这里

\(CDQ\)分治的写法

一个非常大的误区:\(CDQ\)分治一定要归并或边归并边处理。
这其实是不对的,上面说过,只要满足\(CDQ\)分治原则就可以称为\(CDQ\)分治。
所以\(CDQ\)分治的写法其实是多重多样,甚至不拘一格的。

B.CDQ分治中分治原则的确定

\(CDQ\)分治中的一个难点就是确定分治原则。
在我们分治前,我们会确定元素的分治多元组。
分治多元组的确定关键是看分治中对答案的影响因素。
下面给两个非常经典的例题:

例题3:

序列初始为空,接下来每次会在指定位置\(i\)插入一个元素\(x\)
求每次插入后,序列中的逆序对个数。
保证插入元素的值不重复,数据范围\(n \leq 10^5\)

确定三元组\((time , pos , val)\),然后分治:
外部以\(time\)进行排序,消除\(time\)的影响。
然后内部以\(pos\)为原则进行归并,然后怎么处理呢?
考虑一下元素对答案的影响:
每次新增的点对此时答案的贡献为:
(1)插入时间比它早,位置在它前面,值比它大的元素。
(2)插入时间比它早,位置在它后面,值比它大的元素。
所以我们先以\(pos\)从小到大归并排序好。
然后从前往后正着扫一遍,
如果元素的\(time \leq mid\),那么在树状数组中插入它的\(val\)。
如果元素的\(time > mid\),那么在树状数组中查询大于它的\(val\)的元素个数,加入答案。
然后再从后往前倒着扫一遍,处理方法一样。
这样我们最后就得到了每加入一个元素新增的贡献数,最后统计一下答案即可。
具体代码戳我。

例题4:

给定一个大小为\(n*n\)的矩阵,初始每个格子元素都为\(0\)
一共有\(10^5\)个操作,操作如下:
(1)type=1:x y val ,把(x,y)位置的元素加上val。
(2)type=2:x1 y1 x2 y2, 查询(x1,y1)到(x2,y2)这个矩形的元素和。

貌似是上面的 例题1 的升级版....
由二维变为三维,怎么做呢? 其实差不多。
二维前缀和之类的略,自己\(yy\),然后:
建立三元组\((time , x , y)\)
会影响询问答案的为:操作时间早于其,x、y都小于等于它x、y的修改操作
所以外部以\(time\)排序,然后以\(x\)为原则归并。
归并的时候,如果是左边区间的修改操作,把其修改值\(val\)值插入树状数组中的\(y\)位置。
如果是右边的查询操作,查询树状数组中小于等于其\(y\)值的元素值之和。
这题做完了......

C.\(CDQ\)分治的嵌套使用

1.解决四维偏序问题:

首先我们考虑三维偏序的解决方法:
外围排序三元组变为\([L,b,c]与[R,b',c']\),然后归并变为\([L,L,c]与[R,R,c']\)
所以此时只需要在树状数组中查询\(c\)了。
那么四维是不是类似呢?
首先外围排序,四元组变为\([L,b,c,d]与[R,b,c,d]\)
然后我们先以\(b\)为原则归并,但是并不统计答案。
此时我们要记录归并好的序列中每一个元素的\(a\)是属于\(L\)还是\(R\)。
那么此时只有\(b\)是有序的,\(a\)虽然无序但是我们记录了其来源(顺序)。
即此时的四元组为\([L/R,L,c,d]\)与\([L/R,R,c',d']\)
此时把这个新的序列带入下一层\(CDQ\)中再跑一遍,不就是三维偏序的处理吗?
但是这里注意,为了保证\(a\)的顺序,只有\([L,L,c,d]\)与\([R,R,c',d']\)的元素才是合法的。
这个也非常好解决,第一层\(CDQ\)归并时对每个元素打一个标记即可。
具体的代码见这里

2.解决N维偏序问题

这里提个醒,用嵌套\(CDQ\)解决\(N\)维偏序问题的复杂度是\(O(N\ log^{N-1}N)\)的。
其实搞懂了四维偏序的解决方法后,五维、六维....不是一样的吗?
四维时我们打了一层标记,消除了一维的影响,最终变为了三维偏序问题。
那么\(N\)维我们则先归并\(N-3\)遍,打\(N-3\)层标记。
这样一共消除了\(N-3\)维的影响,然后再跑三维偏序即可。
实现五维偏序的具体代码见这里。

一些非常优秀的题目(难度递增):

(1)Luogu P3374 【模板】树状数组 1
(2)BZOJ 3262陌上花开
(3)[SHOI2007]Tree 园丁的烦恼
(4)[CQOI2011]动态逆序对
(5)[HZOI 2016]偏序
(6)[HZOI 2015]改:偏序 II
(7)[BOI2007]摩基亚Mokia
(8)BZOJ 4237 稻草人
(9)[NOI2007]货币兑换Cash

随机推荐

  1. 织梦使用if判断某个字段是否为空

    织梦如何使用if判断某个字段是否为空呢?我们以文章页调用文章摘要为例: 使用if语句判断摘要是否为空,如果有摘要就显示摘要模块,如果没有就不显示 {dede:field.description run ...

  2. WinFom中经典小游戏(含源码)

    最近整理了若干经典的小游戏,无聊时可以打发时间.程序本身不大,练手非常不错,主要是GDI编程,主界面地址如下图所示 源码下载方式 1,关注微信公众号:小特工作室(也可直接扫描签名处二维码) 2,发送: ...

  3. JavaScript 历史漫谈

    话说 JavaScript 其实是诞生在一个特殊的时期,20世纪90年代,正值第三次科技革命时期,互联网刚开始进入人们的生活.设想一下,在网速只有几十比特的那个时代,在登录注册时要花上很长一段时间等待 ...

  4. makefile讲解

    仅供自己学习使用 一.Makefile介绍 Makefile 或 makefile: 告诉make维护一个大型程序, 该做什么.Makefile说明了组成程序的各模块间的相互 关系及更新模块时必须进行 ...

  5. java-redis列表数据操作示例(二)

    接上篇博文<java-redis字符类数据操作示例(一)>,redis连接管理类的代码请跳转查看. 一.列表类型缓存测试类 public class ListTest { /** * 主测 ...

  6. 高可用之KeepAlived(2):keepalived+lvs

    */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...

  7. entity framework core在独立类库下执行迁移操作

    之前学习EFCore的时候,都是在VS创建的默认模板里面进行的,按照官方文档,直接就可以搞定. 今天新项目准备上.Net Core,打算先按照国际惯例,进行分层,数据访问层是用EFCore来做,于是就 ...

  8. xadmin与admin设置

    xadmin : 导入xadmin pip install xadmin 路由设置: import xadmin urlpatterns = [ url(r'^xadmin/', xadmin.sit ...

  9. hihoCoder 树结构判定(并查集)

    思路:树满足两个条件: 1.顶点数等于边数加一 2.所有的顶点在一个联通块 那么直接dfs或者并查集就可以了. AC代码 #include <stdio.h> #include<st ...

  10. 使用基于Android网络通信的OkHttp库实现Get和Post方式简单操作服务器JSON格式数据

     目录 前言 1 Get方式和Post方式接口说明 2 OkHttp库简单介绍及环境配置 3 具体实现 前言 本文具体实现思路和大部分代码参考自<第一行代码>第2版,作者:郭霖:但是文中讲 ...