知识点
-
MST的构造
Boruvka算法常用于解决这类问题:给你n个点,每个点有点权,任意两个点之间有边权,边权为两个点权用过某种计算方式得出,求最小生成树。动图 -
MST上的确定性和存在性问题
-
最小生成树的两个性质:
(1) 不同的最小生成树中,每种权值的边出现的个数是确定的
(2) 不同的生成树中,某一种权值的边连接完成后,形成的联通块状态是一样的
可以用这两个性质做最小生成树计数 -
Kruskal重构树
-
令a,ba,ba,b路径上最长边最短: “最短的最长边”一定在MST上,所以我们求一下MST,再在MST上找a,ba,ba,b路径上的最长边。
-
问nnn个点mmm条边的无向连通图上任两点的最短距离,m−nm-nm−n很小:随便建一棵生成树,把图看成树上挂几条边 CF1051F The Shortest Statement
-
trick: 对区间[l,r][l,r][l,r]操作⇒\Rightarrow⇒ 连边l→r+1l\to r+1l→r+1
题
CF888G Xor-MST
Boruvka + 01Trie树
Boruvka算法简介:
对图中所有的点iii,找到iii连向其它点的最小边,如果这条边还没加进MSTMSTMST,就把它加上。执行完后,把每个连通块缩成点。
不断重复上面的操作,直到只剩下一个连通块。
时间复杂度O((m+n)logn)O((m+n)logn)O((m+n)logn)
此题中,要让ai⊕aja_i \oplus a_jai⊕aj最小,就让ai,aja_i,a_jai,aj的高位尽量保持相等。
假设我们现在运行Boruvka,并且目前只剩两个连通块,那么设ppp表示所有的aaa的第1~(p−1)(p-1)(p−1)位(从高位数起)都相等,在第ppp位才出现不同。
那么在这两个连通块中一定不会有 连接i,ji,ji,j且a[i]a[i]a[i]的第ppp位和a[j]a[j]a[j]的第ppp位不同的边 ,也就是说aaa值的第ppp位为1的点构成一个连通块,aaa值的第ppp位为0的点构成一个连通块。
那么最后加入的边一定是 连接i,ji,ji,j且a[i]a[i]a[i]的第ppp位和a[j]a[j]a[j]的第ppp位不同的边,用 01Trie树 找到这样的最小边。
加完最后一条边,我们再递归回去,把aaa值的第ppp位为1的点连成一个连通块,aaa值的第ppp位为0的点连成一个连通块。
Code
CF1108F MST Unification
Blog
Code
CF733F Drivers Dissatisfaction
根据贪心,把SSS的费用全部用来降一条边的权值不比用来降多条边的权值劣。
枚举每一条边,看一下降这条边的答案是多少,最后取最优结果即可
先用Kruskal建出MST,设sumsumsum为MST里各边的权值和。
-
如果降树边:
答案为sum−⌊Sci⌋sum-\lfloor\frac{S}{c_i}\rfloorsum−⌊ciS⌋(保证降完后该边是在MST里的) -
如果降非树边:
把降完权值后的非树边连上,原MST上出现一个环,我们找到环上最大的边删掉即为新的MST。
每条非树边对应的环上最大边边权可以用倍增预处理出来。
Code
CF1416D Graph and Queries
Kruskal重构树
不会删边。所以考虑离线,按时间倒序进行操作,删边变成加边。
但是遇到的麻烦是,操作 1 是正序进行的,如果我们倒序操作,就不知道当前哪些点 pu=0p_u=0pu=0 了。
解决方法是,先倒序遍历一遍所有操作,按“加边”的顺序,建出重构树。重构树优美的性质是,对任意一个节点 v,在某个时刻之前和它连通的节点,恰好在重构树上 v 的某个祖先的子树中。并且我们可以通过树上倍增,在 O(logn)O(logn)O(logn) 的时间内找到这个祖先。
建出重构树后,我们回到正向的时间线。按正序处理所有询问(操作 1)。前面说过,在某个时刻和 v 连通的节点,在 v 某个祖先的子树中。先倍增找到这个祖先。它的子树是 dfs 序上连续的一段。我们预处理出重构树的 dfs 序,那么问题转化为求区间最大值,支持单点修改。可以用线段树维护。
Code
CF1253F Cheap Robot
法一:
先预处理出disudis_udisu表示uuu到最近的充电中心的距离(求多源最短路戳这)
从aaa到bbb的路径 能经过边(u→v,w)(u\to v,w)(u→v,w),当且仅当c−disu−w≥disvc-dis_u-w\geq dis_vc−disu−w≥disv,即disu+disv+w≤cdis_u+dis_v+w\leq cdisu+disv+w≤c。
那么问题变成求一条从aaa到bbb的路径使得路径上每条边的disu+disv+wdis_u+dis_v+wdisu+disv+w的最大值最小(明显是满足条件的最小的ccc)。
可以用Kruskal重构树实现,
也可以用NOIP2013货车运输/BZOJ3732 Network的套路实现:
“最短的最长边”一定在MST上,所以我们求一下MST,再在MST上找a,ba,ba,b路径上的最长边。
法二:
在任意两个充电中心i,ji,ji,j之间连边,边权di,jd_{i,j}di,j为原图上i,ji,ji,j之间的最短距离。
那么ccc为新图中从aaa到bbb的路径上最长边的最小值。法一中已经解决了这个问题。
现在的问题是如何求di,jd_{i,j}di,j,这里介绍一种剪枝方法:
先跑多源最短路,对每个点iii求出离iii最近的充电中心fif_ifi和到fif_ifi的最短距离disidis_idisi,然后枚举原图中的每条边(u→v,w)(u\to v,w)(u→v,w),在新图上连边fu→fvf_u\to f_vfu→fv,边权w′w'w′为disu+disv+wdis_u+dis_v+wdisu+disv+w,
可以证明minfu→fv{w′[fu→fv]}=du,vmin_{fu\to fv}\{w'[fu\to fv]\}=d_{u,v}minfu→fv{w′[fu→fv]}=du,v。同样的剪枝方法见这里。
Code
CF1051F The Shortest Statement
m−n<=20m-n<=20m−n<=20,所以可以看成是一棵树上挂了几条边
树上求两点间最短距离用LCA
多出来的边怎么办?
找出所有非树边的端点记为特殊点,枚举u,v间路径过每一个特殊点的情况
(u,v路径要过非树边一定过特殊点,枚举过点的情况是因为可以用dijkstra)
因为u,v间路径只有 只过树边 和 不是只过树边 2种,所以一定不会漏(不保证不重,但没关系)
Code
CF1120D Power Tree
Blog
Code