『正睿OI 2019SC Day2』
<更新提示>
<第一次更新>
<正文>
分治
普通分治
普通分治是指针对序列或平面问题的分治算法。
思想
普通分治的思想是指将一个序列问题或平面问题通过某种划分方式划分为若干个子问题,直到子问题规模足够小,可以直接回答,再通过合并得到原问题的解。
通常来说,我们划分的方式是某个与题目特征有关的值,例如最大值或最小值。而当不易使用特征值进行划分的时候,我们也可以直接使用中点来划分,以保证时间复杂度,再设法计算贡献。
重要例题和简要题解
\(1.\) 求序列所有子区间的最大值之和:利用最大值进行划分,计算每一个最大值的贡献即可。
\(2.\) 求序列所有子区间的最大值最小值乘积之和:利用中点划分,讨论最大最小值和中点的相对位置关系,预处理计算贡献即可。
\(3.\) 求序列所有子区间的\(gcd\)之和:发现\(gcd\)只有不超过\(log\)种取值,利用中点划分,分段计算贡献即可。
整体分治
整体分治指的是在带若干个询问,修改的动态问题中,对所有询问整体二分答案的分治算法。
思想
整体分治会对多个操作同时进行二分答案,再通过操作分类的方式,得到子问题,再向下递归求解,直到值域区间足够小,可以直接回答询问。
其精髓在于整体上的二分答案,以及询问的分类操作。当问题还带有修改操作时,我们还需对修改操作进行分类,这就需要根据操作的影响来分类,还需要用数据结构记录影响。
重要例题和简要题解
\(1.\) k大数查询:对所有操作进行整体分治,对于一个询问,根据比\(mid\)值大的数的个数进行分类,对于一个添加操作,就看添加的数是否大于\(mid\)来计算影响,进行分类。
CDQ分治
\(CDQ\)分治指的是针对时间划分来计算修改操作影响的分治算法。
思想
形式化的,\(CDQ\)分治用来解决偏序问题,核心思想就是每次分治通过部分有序的特征化简偏序限制,回答询问,计算影响,再进行归并排序。其关键在于如何抽象出其偏序的关键字,来进行分治,还有影响以及询问的计算。
更一般的,\(CDQ\)分治可以在偏序问题中顶替一层数据结构,在动态问题中可以利用时间分治的技巧转动态为静态,是一种使用范围较广,比较广义的分治算法。
重要例题和简要题解
\(1.\) 陌上花开:三维偏序模板题,可以先排序一维,\(cdq\)分治维护一维,树状数组维护一维来统计答案。
\(2.\) 好朋友的题:矩阵和用二维前缀和拆一下,拆成\(4\)个不同贡献的询问,然后\(cdq\)分治维护修改的影响和计算答案即可。
点分治
点分治指的是树上利用重心作为划分点,进行统计的分治算法。
思想
点分治是对树上有关信息进行统计的算法,其划分在于每一次选取树的重心,统计有关重心的答案,然后再删除重心,将树分为若干个不同的子树,递归处理。
而点分治的关键在于如何快速对有关重心的答案进行统计,通常来说会分为两种方法:\(1.\) 用数据结构维护统计 \(2.\) 用单调性和尺取法统计。而在使用第二种方法的时候,又会涉及到同一棵子树内的答案,需要我们重新计算容斥掉,这就在于具体的处理。
重要例题和简要题解
\(1.\) 求所有边数小于等于\(L\)的链的长度之和:每一次以重心分治,用尺取法统计答案,用树状数组维护长度和,相同子树内的答案要容斥掉。
图论
最短路及有关算法
最短路算法是由\(bfs\)拓展而来的,通常来说,\(bfs\)算法可以解决如下的最短路问题:
\(1.\) 普通的队列\(bfs\)可以解决边权唯一的最短路问题
\(2.\) 双端队列\(bfs\)可以解决\(0/1\)权最短路问题
\(3.\) 拆边后\(bfs\)可以解决小边权最短路问题
而一般的图中我们会使用如下的最短路算法:
\(1.\) \(Dijkstra\)可以在非负权图上用\(O(nlogm)\)的时间内解决单源最短路问题。
\(2.\) \(Bellman-ford\)算法可以在无负环图上用\(O(nm)\)的时间解决单源最短路问题。
\(3.\) \(SPFA\)算法可以在无负环图上用期望\(O(mloglogn)\)的时间解决单源最短路问题。
\(4.\) \(Floyed\)算法可以在\(O(n^3)\)的时间内解决全源最短路问题。
最短路算法还可以解决如下几个拓展问题:
\(1.\) 负环判定问题:在\(SPFA\)中,如果一个点入队次数超过\(n\)次,则说明图中存在负环。
\(2.\) 差分约束问题:不等式可以转化为三角形不等式的形式,于是建图用最短路求出一组最值解。
重要例题和简要题解
\(1.\) 次短路:可以从起点和终点各跑一遍单源点最短路,然后枚举每一条边,由于第二短路至多只有一条边不在最短路上,所以我们强制选这条边,用两个端点到起点终点最短路更新答案即可。
\(2.\) 逛公园:我们可以先预处理出最短路,然后用类似于最短路的状态进行\(dp\):\(f[x][k]\)代表到了点\(x\),走过的距离为\(dis[x]+k\)的方案数,利用预处理的最短路转移即可。
最小生成树及有关算法
一般来说,我们会使用如下的最小生成树算法:
\(1.\) \(Prim\)可以在\(O(nlogm)\)的时间内求出无向图的最小生成树。
\(2.\) \(Kruskal\)可以在\(O(mlogm)\)的时间内求出无向图的最小生成树。
重要例题和简要题解
\(1.\) 询问数列:区间和可以转化为前缀和相减的形式,于是询问区间和\((l,r)\)等价于建立了点\(r\)和点\(l-1\)之间的联系,若知道其中一个,可以得知另一个,那么根据询问代价建图,求最小生成树即可。
图论计数及有关算法
数环问题
\(1.\) 三元环计数:求出每个点的度数,将点以度数为第一关键字,标号为第二关键字排序,然后重构图:排名小的点向排名大的点连边。然后枚举两条边构成的路径,统计答案即可。
\(2.\) 四元环计数:用同样的方式重构图,然后再枚举路径时先枚举一条无向边,再枚举一条有向边,利用数组记录统计答案即可。
prufer序
\(prufer\)序是一种可以与带标号无根树形成双射的整数序列。
\(1.\) 树转序:每一次找标号最小的叶子节点删除,并把与其相邻的节点加入到序列中。
\(2.\) 序转树:每一次找不在树中的最小标号节点,将其和序列头连边并删除序列头。
例题:给定无根树中每一个点的度数,求这样的无根树有几棵?
给定一个点的度数相当于给定了这个点在\(prufer\)序中出现的次数加\(1\),于是就变成了数列计数问题,可以直接得到答案。
二分图及有关算法
二分图判定
染色法可以在\(O(n)\)的时间内判定一张图是否为二分图。
二分图匹配
\(1.\) \(Hungary\)可以在\(O(nm)\)的时间复杂度内找到二分图的最大匹配。
\(2.\) \(Dinic\)可以在\(O(n\sqrt m)\)的时间复杂度内找到二分图的最大匹配。
二分图的最小点覆盖和最大独立集
\(1.\) \(|\)二分图的最小点覆盖\(|\) \(=\) \(|\)二分图的最大匹配\(|\)
\(2.\) \(|\)二分图的最大独立集\(|\) \(=\) 点总数 \(-\) \(|\)二分图的最小点覆盖\(|\)
Hall 定理
设\(S\)是左边点的一个子集,设\(N(S)\)为\(S\)所有点邻居的并集,则一个二分图存在完美匹配的充要条件是:\(\forall S\ ,\ |S|\leq |N(S)|\)
重要例题和简要题解
\(1.\) 选择:对于\(x_i,y_i\),连接\((x_i,y_i)\),对于每一个联通块:若\(M>N\),则无解,若\(M=N\),则方案数为\(2\),若\(M=N-1\),则方案数为\(n\)。
\(2.\) 消毒:发现最优策略即每一次消一行或一列,那么对于病毒\((x,y)\),要么消第\(x\)行,要么消第\(y\)列,于是连接\((x,y)\),求二分图的最小点覆盖即可。
字符串
KMP
\(KMP\)算法可以在线性时间内解决字符串匹配问题。
重要例题和简要题解
\(1.\) 求一个串的最小循环节:先用\(KMP\)自我匹配,发现最小循环节就是\(n-fail[n]\)。
\(2.\) 求出每个前缀的不重叠相等前后缀数量:先用\(KMP\)求出\(fail\)数组,则对于一个\(x\),点\(x\)向点\(fail[x]\)连边,然后在树上路径上二分查找即可。
\(3.\) 构造字符串:采用贪心策略:第一个字符没有限制,就选最小的字符,之后的每一个字符,如果\(fail\)数组不为\(0\),则直接确定字符,若\(fail\)数组为\(0\),那就选取合法的最小字符。
SA
一般来说,我们会用如下方法求解后缀数组:
\(1.\) 倍增法可以在\(O(nlogn)\)的时间内求解后缀数组。
\(2.\) \(DC3\)法可以在\(O(n)\)的时间内求解后缀数组。
<后记>