斯坦纳树
不看 c 9 \sf c9 c9 的博客我都不知道有这玩意儿。属实是菜到某种境界了。
目标是,在非负边权的无向图中,选出边权和最小的连通子图,使得 k k k 个给定点在图中。显然只会选出树,树则有根。于是可以 d p \tt dp dp,记 f ( x , S ) f(x,S) f(x,S) 为 x x x 为根的树去连通 S S S 内的关键点的最小代价。
根只有一个儿子则 f ( x , S ) ← f ( y , S ) + w ( x , y ) f(x,S)\gets f(y,S)+w(x,y) f(x,S)←f(y,S)+w(x,y),有多个儿子则 f ( x , S ) ← f ( x , T ) + f ( x , S ∖ T ) ( T ≠ ∅ , T ⫋ S ) f(x,S)\gets f(x,T)+f(x,\;S\setminus T)\;(T\ne\varnothing,\;T\subsetneqq S) f(x,S)←f(x,T)+f(x,S∖T)(T=∅,T⫋S) 。
转移顺序,显然可以按照 ∣ S ∣ |S| ∣S∣ 升序。或者按照 S S S 升序,二进制值意义下。然后做第二类转移是容易的,子集枚举嘛。
第一类转移就是最短路转移,可以直接 dijkstra \texttt{dijkstra} dijkstra 等。
卡常术:应让 S S S 为数组的第一个维度,这样最短路时的内存访问很连续。同时,第二类转移也该先枚举 T T T 再枚举 i i i 。
最小直径生成树
枚举直径的中点,则答案是它到所有点的最远距离乘 2 2 2 。如果这不是中点,则该值偏大。
故只需找到图的中点,即最小化它到所有点的最远距离。
求出全源最短路,然后在每条边上找最优点。是个 O ( n ) \mathcal O(n) O(n) 段分段函数,每段的 min \min min 易求。复杂度 O ( n m ) \mathcal O(nm) O(nm) 。
最小树形图
目标是,在带权有向图上,找出边权和最小的子图满足 r r r 到每个点都存在唯一路径(即树形图)。边权是否非负不重要,因为给所有边加上大正数即可。
因为它比较像最小生成树,所以可以贪心。有点不自然?但是逻辑是相对严谨的。这被称作朱刘算法。
树形图等价于给除了 r r r 的每个点选择入边,使得图中无环。因此,给 r r r 以外的点选择最小权入边,如果这可以构成树形图,那肯定是最优解。
否则(存在最优解使得)该环上必有恰好一条边不选。因为一条边不选,说明无法调整,说明两侧的点有与这条边相反的可达关系;若环上有至少两条边不选,就能推出环形的可达关系,矛盾。
因此该环可以缩为单点,外部向内的连边变为要替换原定入边,即减去原定入边的边权。这个新的单点也只选择一个入边,即替换一条边,符合预期。
每轮缩点至少能缩掉一个点(并不是所有点都在环中,可能只有两个点成环,其余点构成树形图),总复杂度 O ( n m ) \mathcal O(nm) O(nm),实际运行应该跑不满。
还有个 tarjan \textsf{tarjan} tarjan 算法。让所有点向 r r r 的连边,边权为 + ∞ +\infty +∞,不难验证这个流程是正确的:维护一个栈,初始时其中有任意点 a a a,每次取出栈顶与其最小入边,选择该入边。若已选择的入边构成环,显然是栈顶的若干点,缩点(新点仍放于栈顶),继续该流程直到整张图只剩一个点。
用并查集处理缩点,用可并堆(带标记)维护每个点的入边即可。复杂度 O ( m log m ) \mathcal O(m\log m) O(mlogm) 。
Comment. 奶奶滴,为什么我算出来的复杂度和 OI-wiki \text{OI-wiki} OI-wiki 里写的不一样 😢
最小环
如果图有向,等价于从自己走到自己的最短路。用 Bellman-ford \text{Bellman-ford} Bellman-ford 可以做到 O ( a n s ⋅ n 2 ) \mathcal O(ans\cdot n^2) O(ans⋅n2),显然 a n s ans ans 不超过 n n n 。
如果图无向,则可以枚举一条边并删去之,再做最短路做到 O ( m 2 log m ) \mathcal O(m^2\log m) O(m2logm) 。
稠密图里更好的做法是,枚举环上编号最大的点,在 Floyed \texttt{Floyed} Floyed 过程中直接更新。复杂度 O ( n 3 ) \mathcal O(n^3) O(n3) 。
BEST \text{BEST} BEST 定理
有向欧拉图
G
G
G 的欧拉回路数量是
T
∏
v
∈
V
(
deg
(
v
)
−
1
)
!
T\prod_{v\in V}(\deg(v)-1)!
Tv∈V∏(deg(v)−1)!
其中 T T T 是任意节点为根的根向树(或叶向树)的数量, deg ( v ) \deg(v) deg(v) 是 v v v 点的出度(或入度)。
其实就是 T T T 选择了每个节点最后一次离开的边,然后之前的离去都可以 ( deg ( v ) − 1 ) ! (\deg(v)-1)! (deg(v)−1)! 任选。根节点(回路起点)其实有 deg ( v ) ! \deg(v)! deg(v)! 个选择,但是每条回路会被统计 deg ( v ) \deg(v) deg(v) 次(根节点在其一截断)恰好要除以它。
感觉发现它的人还是比较有洞见力的。首先每个节点的最后一次离开不能成环,否则永远触发不了。
而不成环的时候,考虑每个点的最后一次离开:此时它的入度应该被用光,故它在根向树中的子节点走过了最后一条边。以此类推,可知它在根向树中的整棵子树都走过了最后一条边。
因此,如果内向树边不存在,则点无需再走。这说明所有仍然需要经过的点构成连通图,故而有欧拉路径,故总可以走出欧拉回路。非常奇妙。
k k k 短路
最后的提交记录停留在 2021/9/18 \texttt{2021/9/18} 2021/9/18,只有 44 44 44 分。史前巨坑了!
第 k k k 个最值,经常是 “将所有方案建立堆,从根节点开始 b f s \tt bfs bfs 查找” 的方法。
最小就是真实最短路。次小,则是选择一条非最短路上的边,然后强迫自己走过这条边。
完全意义上地说明它:对于非最短路边 ⟨ a , b ⟩ \langle a,b\rangle ⟨a,b⟩,它给最短路带来增量 w + d i s ( b ) − d i s ( a ) w+dis(b)-dis(a) w+dis(b)−dis(a) 。这样的边沿着最短路顺序进行选择。
因此,接下来可以怎样调整,就是问所有可用的非最短路边。这样就把所有方案的堆建了出来。
可用的非最短路边太多(堆中的子节点太多),因此也要建立堆(平衡树当然也可以,但没必要)。由于可用的非最短路边就是当前点的最短路上的非最短路邻边,因此用可持久化堆维护。
那么所有可用的后继状态就是:当前值是多少,能在某个点为根的堆里去选元素(为了模拟出 p o p \tt pop pop 的效果)。时间复杂度 O ( ( n + m ) log m + k log k ) \mathcal O((n+m)\log m+k\log k) O((n+m)logm+klogk),空间复杂度 O ( m log m + k ) \mathcal O(m\log m+k) O(mlogm+k) 。