7.29
上午
分治
普通分治
• 通过将区间分成两个区间,来将问题分成两个子问题
例题:
• 求所有区间的最大值之和
根据分治思想,对于每个区间,都递归分成两部分,边界为1
对于每个区间,先考虑固定左端点 l ,O(n) 求出以 l 为左端点的跨越 mid 的最大值
每次O(n) 求 l ~ mid 的最大值 maxl, 再用 maxr 记录右边第一个比 maxl 大的位置,然后求最大值前缀和 f[i]
则答案为 (maxr - 1 + (mid + 1) + 1) * maxl +f[r] -f[maxr - 1]
再考虑固定右端点同理
• 求所有区间的最大值*最小值之和
四种情况:
1、最值都在左边
枚举 l , 计算有几个 r 满足条件
Ma[l] : 最大的 r 使得 l ~ r 的最大值在 l ~ mid 中
Mi[l]:最大的 r 使得 l ~ r 的最小值在 l ~ mid 中
显然, Ma[i] , Mi[i] 非严格单调
r = {mid + 1, …,min(Ma[l], Mi[l]) }
|r|:r的方案数
所以答案为 |r| * max( l, …, mid) * min( l, …,mid)
2、最大值在左边,最小值在右边
∑ r = M i [ l ] + 1 M a [ l ] \sum_{r = Mi[l] + 1}^{Ma[l]} ∑r=Mi[l]+1Ma[l]max( l,…, mid) * PMi[r]
=max( l,…, mid) * ∑ r = M i [ l ] + 1 M a [ l ] \sum_{r = Mi[l] + 1}^{Ma[l]} ∑r=Mi[l]+1Ma[l] PMi[r]
3、最值都在右边
4.最小值在左边,最大值在右边
剩下两种情况同理
• 求所有区间的 gcd 之和
gcd(L , … , R) = gcd(gcd(L , … , Mid) , gcd(Mid + 1 , … , R))
枚举 L 在左边哪一段, R 在右边哪一段
当 R 固定时,随着 L 的变化暴力分治求gcd
• 求二维平面上最近点对
按 x 排序,从中间切开
先得到左边和右边分别的最近点对距离 d1,d2
得到 d = min(d1,d2)
以中间线为一边,在左右两边分别构造宽(x)d,长(y)2d 的矩形,再暴力得到一点在左矩形,一点在右矩形的最近点对
得到最终解
十分神奇的一个结论,中线一侧的矩形中期望有6个点
• 分治多项式乘法
分开乘
例题
旅行者
ZJOI2016
• 给定⼀张 n*m 的带正权网格图,有 Q 组询问,每次询问两对点之间的最短路
• 1<=n*m,Q<=50000
离线操作
把矩形沿较长边切成两半,然后枚举询问
若询问的两点在同侧,则继续递归
否则询问答案必定会被更新
dis[a][b] = min(dis[a][b], dis[a][x] + dis[x][b])
连续区间
• 给定⼀个排列 p[1…n],求有⼏个区间 [L,R] 满⾜ p[L…R]排序后是连续的
• n<=500000
即max(L, … ,R) - min(L, … ,R)+1 = R - L + 1
然后可以转化为 r = max - min + l
枚举左端点 l,得到左边的最大值 maxl 和最小值 minl,求得右边第一个大于 maxl 的位置 minr 和第一个小于 minl 的位置maxr
分类讨论:
1.r ∈ [mid + 1, min(maxr, minr) - 1], 即最值都在左边,判断 maxl - minl + l 是否在范围内
2.r ∈ [min(maxr, minr), max(maxr, minr)] , 即最值一个在左边一个在右边。
假设 maxr > minr, 则最小值与左边无关,处理出 mid + 1 ~ r 之间每个点的前缀最小值,判断 r ~ max 的范围和 l ~ min 的区间交
3.maxr < minr 同理,求前缀最大值
4.r ∈ [max(maxr, minr), R] , max - min 随 r 增大而变大,求区间交
然后递归处理子区间
XOR最小生成树
• 给定 n 个点,第 i 个点的点权是 a[1…n],现在定义边 (i,j)的权值是 a[i] xor a[j],求最小生成树
将所有的数字排序,再用01字典树维护
用最高位不同将数字分开来,而内部数字则用同样的方法进行异或连边
区间统计
• 给定 a[1…n],求有⼏个区间 [L,R] 满⾜ a[L] or a[L+1]…or a[R]>max(a[L…R])
• N<=3*10^5
• a[i]<2^30
x = max 即对于可行区间,y or x = y
枚举最大值 x
统计 x 向左/向右 x or a[i] = x ,满足条件的 i 的最大最小值
相乘求区间数
二分
• ⼆分其实是对答案分治
• 分数规划问题
有一些二元组(ai,bi),从中选取一些二元组,使得∑ai/∑bi最大(最小)
每次二分 mid,判断∑ai/∑bi >= mid
转化为:∑(a[i]−b[i]∗mid)>=0
然后check贪心
• 给定 n 个点 x[1…n],要求将它划分成尽量少的连续区间,使得每个区间的最小圆覆盖的半径<=S
求最小覆盖圆是O(n)的
所以 check(l,l + 2i) 找到二分的上界,再进行二分
复杂度为 O(n log n)
整体二分
• 类似分治,所有询问一起做
K大数查询
ZJOI
• 有 N 个位置, M 个操作,每次操作是 1 a b c,或者 2 a b k
• 1 a b c 表示在第 a 个位置到第 b 个位置每个位置都加入⼀个数 c
• 2 a b k 表示询问第 a 个位置到第 b 个位置的第 k 大的值
• N,M,c<=50000
二分 mid,判断每个询问答案是否≤mid
每个询问 K 小值 ≤mid
即 ≤mid 的数有没有 K 个
当遇到的操作是加入操作时,把要加入的数和mid比较
用树状数组维护每个被修改区间的 c 的个数
CDQ分治 [坑]
• 把 [L,R] 分成 [L,mid] 和 [mid+1,R]
• 考虑左边对右边的贡献
• 三维偏序
• 矩阵加,矩阵求和
• 缺 1 背包问题:给定 n 个物品的重量 W[i] 和价值 V[i], Q次询问,每次询问对除了第 i 个物品以外的物品 01 背包后重量不超过 S 的最大价值和
n,W,V<=2000. Q<=1000000
• 缺点最短路问题:给定 n 个点的带权无向图, Q 次询问,每次询问 X 到 Y 的不经过 Z 的最短路长度
N<=200, Q<=1000000
• 解递推式 f[n]=sum( f[i]*g[n-i] )
点分治[坑]
• 区间分治的复杂度保证是由于每次分成<=n/2的区间
• 树每次分成<=n/2的⼦树
• 点分治,是一种针对可带权树上简单路径统计问题的算法。
• 分治点一般为重心
• 每一次找到重心,递归的子树大小是不超过原树大小的一半的,那么递归层数不会超过O(logn)O(logn)层,时间复杂度为O(nlogn)
• 求所有边数<=L 的链的权值之和
• 给定⼀棵树,有 Q 次询问,每次询问离 x 距离 <=L 的点数
• 给定⼀棵树,每个点有物品重量 W[i] 和价值 V[i],求价值最大的重量不超过 S 的连通块, n,S<=2000
图论
图论基础术语
• 出度,⼊度,度数
• ⽆向图,有向图,欧拉回路,哈密尔顿回路,环,简单环
• 连通块,强连通块,点双连通分量,边双连通分量
• 深度优先搜索,⼴度优先搜索
BFS
• 给定⼀张有向图,每条边的⻓度为 1,求 1 号点到其他点的最短路⻓度
• 给定⼀张有向图,每条边的⻓度为 0 或 1,求 1 号点到其他点的最短路⻓度
• 给定⼀张有向图,每条边的⻓度为 1 或 2,求 1 号点到其他点的最短路⻓度
bfs直接做就可以了
Dijkstra
• 原理:当前 d[x] 最小的 x ⼀定已经确定了最短路
• 扩展:当 dis[x] 的大小在 10^7 内时怎么做
把优先队列换成链表 把log强行去掉
• 缺点:图中不能有负权
Bellman-Ford算法
适用前提:没有负环(或称为负权值回路),但可以有负权
可以用来判断是否有负权回路
SPFA 算法
• ⽤⼀个队列维护有哪些点等待更新,优化了Bellman-Ford
• 每次取出⼀个点 x 去更新所有出边 (x,y,w),如果 y 被更新了就压⼊队列
• 时间复杂度: O(nm)
(如何卡SPFA算法:搞个⾏很少的⽹格图,竖着的边权很⼩,横着的边权很⼤)
具体可见https://blog.csdn.net/yfzcsc/article/details/77623365
判断是否存在负环
• 判断最短路的边数是否>n
• 判断⼀个点是否⼊队超过 n 次
Floyd 算法
• 给定⼀张 n 个点 m 条边的正权有向图,求每两个点之间的最短路
• 设 F(K,X,Y) 表示 X 到 Y 的路径中,满⾜路径上的点的标号都不超过 K 的最短路径
• F(K,X,Y)=Min( F(K-1,X,Y) , F(K-1,X,K)+F(K-1,K,Y) )
• 时间复杂度: O(n^3)
差分约束问题
• 最短路问题的约束:
• 对于 (x,y,w), d[y]<=d[x]+w
• 最短路问题可以给出这⼀类不等式的最⼤解
次短路
• 如何求出 1 号点到 N 号点的次短路
• 定义:若 d[v]=d[u]+w(u,v),则称 (u,v) 是最短路图上的边
• ⼀条次短路⼀定⾄多经过⼀条⾮最短路图上的边
[NOIP2017] 逛公园
• 给定⼀张有向带正权拓扑图,求有几条 1 到 N 的路径的长度<= 1 到 N 的最短路+K
• N,M<=105, K<=100,边权<=109
dis[i] 表示 从 1 号点到第 i 号点的最短路
f[x][L] 表示当前走到节点x,还能在原先最短路的基础上多走L
f[x][L]中 L 的有效取值为dis[1,x] ~ dis[1,x] + k,共k + 1 种
然后DP
要注意过程中还要用spfa判环
最短路变种
• 给定⼀个带权有向图, Q 次询问,每次询问删掉某条边后 1到 n 的最短路
如果删去的边不是在原图的最短路上的,则没有影响
而如果删去的边是在最短路上的,我们可以先预处理出一条左端点在这条边的左端点前,右端点在这条边的右端点后的边,且长
度最小
• 给定⼀个带权有向⽆环图, Q 次询问,每次询问删掉某个点后 1 到 n 的最短路
强连通分量
• 在做 Tarjan算法时,如果 tarjan(x) 后发现 dfn[x]=low[x],则x 的⼦树⾥的剩下的所有点构成⼀个强连通分量
数环
• 给定⼀张 n 个点 m 条边的⽆向图,求三元环个数
• 给定⼀张 n 个点 m 条边的⽆向图,求四元环个数
• n,m<=50000
最小生成树
• 给定⼀张 n 个点的带权⽆向图,求权值和最⼩的⽣成树
• Kruskal 算法:将边按照权值⼤⼩从⼩到⼤排序,之后能加就加,⽤并查集维护
• 证明:证明权值最⼩的边⼀定在最⼩⽣成树中,然后归纳法
• 你现在很想知道⼀个数列 A[1…N] 是啥,但是需要花费代价去获取情报,你可以花费 Cost[L][R] 的值去得到 A[L…R]的和,给定 Cost,求最少花费多少代价才能确定 A[1…N]
• N<=1000, Cost[L][R]<=109
Prufer序列
• 将⼀棵树变成⼀个序列:
• 每次选择树上标号最⼩的叶⼦,删掉它,将与它相连的那个点的标号加到序列⾥,直到只剩下 2 个点
• 可以证明:任意⼀个⻓度为 n-2 的 1…n 的序列都是某棵树的 Prufer 序列
• 所以可以推出: n 个点的⽆根树个数为 n(n-2)
• 每一个Prufer序列和一棵树双向映射。
• 给定每个点的度数 d[i],求有几棵这样的无根树。
假设一个点入度为d,它最多有可能在prufer上出现(d-1)次
一共有n-2个数字出现在prufer上,其中每个相同数字出现d-1次,所以答案为(n - 2) ! / ( (d1 - 1)! (d2 - 1)! ……(dn - 1)! )
⼆分图[坑]
• 可以分成两部分,使得这两部分内部没有边的图
• ⼀个图是⼆分图等价于该图没有奇环
• prob1. 判断⼀张图是否有奇环
O(N)
• prob2. 判断⼀张图是否有偶环
• 最⼩顶点覆盖:选最少的点覆盖所有边
• |⼆分图最⼩顶点覆盖| = |⼆分图最⼤匹配|
• 最⼤独⽴集:选最多的点使得它们两两没边相连
• |⼆分图最⼤独⽴集| = 总点数 - |⼆分图最⼩顶点覆盖|
Hall 引理[坑]
• 设 S 是左边点的⼀个⼦集,设 N(S) 为 S 所有点邻居的并集,则⼀个⼆分图存在完美匹配的充要条件是:
• 对于所有 S, |S|<=|N(S)|
• 给定⼀个 [n,n] 个点的⼆分图,每条边有边权,要求删去边权和最⼩的边集,使得这张图没有完美匹配
• n<=18
选择
• 给定 n 个数对 (x[i],y[i]),你需要构造⼀个数组 s[i],满⾜ s[i]是 x[i] 和 y[i] 中的其中⼀个,且 s ⾥没有重复元素,顺便数个⽅案数
• 1<=n<=10^6
边的染⾊
• 给定⼀张⽆向图,边有边权且为0或1,有些边的边权还没有确定,现在需要你确定这些边的边权,使得满⾜所有环的边权的异或值都为0,求⽅案数
• 1<=n,m<=10^5
边权转点权
存在a[1…N] 对于(x,y,w) 有a[x] xor a[y] = w
S:已确定的边构成的连通块个数
• 有⼀个 N * M 的矩形,其中有 K 个格⼦中有病毒,现在你可以进⾏若⼲次消毒,每次你可以选择⼀个任意⼤⼩的⼦矩形进⾏消毒,假设是 A *B 的矩形,则代价是 min(A,B),要求你⽤最少的代价进⾏消毒
• N,M,K<=5000
字符串[坑]
KMP算法
• fail[N]: s[1…N] 最⻓的相等的前后缀
• bobocow:
• fail=[0,0,1,2,0,0,0]
• 求⼀个串的最⼩循环节 , = N - fail[N]
对于所有i, 有s[i] = s[i - p]
NOIP2014 动物园
• 给定⼀个字符串 S,对于每个前缀S[1…i]求出:有⼏对前后缀相等且不重叠. |S|<=1000000
GT考试
• 给定⼀个 m 位数字串 S,求有⼏个⻓度为 n 的字符串 T,满⾜ S 不是他的⼦串. m<=20. n<=10^9
• 有⼀个串 S,给定 S[1…i] 的最⼩循环节 d[i],构造⼀个字典序最⼩的S. |S|<=10^6
• 有⼀个串 S,定义⼀个优秀的拆分是将⼀个串表示成 AABB 的形式,求 S 的所有连续⼦串的优秀的拆分的个数之和. |S|<=2000
• Trie上的KMP: AC⾃动机
后缀数组【巨坑,先放着】
• 给定⼀个串 s[1…n],将所有后缀排序
• rk[i]: s[i…n] 的排名
• sa[i]: rk 为 i 的后缀是哪个
• 倍增+归并排序实现
• height[i]: S[sa[i]…n] 和 S[sa[i+1]…n] 的LCP
• height[i]>=height[rk[sa[i]-1]]-1
后缀数组常规操作
• LCP(X,Y)=Min(H[rk[x]]…H[rk[y]-1])
• 最⻓重复⼦串
• 不可重叠最⻓重复⼦串
• 本质不同的⼦串个数
• 求 S[l…r] 的出现次数
总结
还是有很多的坑要填。
分治的话,CDQ啊,整体分治什么的都不是很理解
图论总体还好,毕竟基本学过,但二分图,hall引理等还有一些没怎么懂
字符串大写懵,后缀数组是毒瘤
还是多看看其他大佬的博客,加深理解吧