斜二倍增 x Dinic = O(VE \log^2 V)
yangzhe1990 · · 算法·理论
一、Dinic 算法的已知 O(VE \log V) 实现
网络流的 Dinic 算法中,做好残量网络的分层图
- 计算增广路流量、找到边权恰好等于流量的一条边,这条边需要被删除;
- 对于要删除的边,进行删除,将这条边的流量作为最终结果存储下来;删除掉的边对应的下一层结点,选择下一条边连向上一层,如果不存在边了,这个结点也需要删除,它到下一层结点的所有边也需要删除,影响的更下一层的结点也需要进行维护;
- 将整条增广路(现在已经分成了两段)标记减小残量,实现为,对整条增广路的流做区间加法。
其中步骤 2、3 也可以换一个顺序进行操作。传统上用 Link-Cut Tree(下文缩写为 LCT )做上面的操作,时间复杂度
:::info[图及 URL] 动态树优化的 Dinic 最大流算法 这篇文章中的图(如下)很直观了。 也有洛谷的文章简述。 :::
用可并二叉树操作链,不考虑树结构
考虑其实是在做 DFS(并且有当前弧优化),找到了从源到汇的一条链。然后操作。之后删除掉边了以后,就断开了一截链。之后 DFS 选择了一条新的边的时候,如果是从中间连接到了某个之前断开了的链,就是把这个链再切一下,然后后面的部分连接上。(相当于 LCT 的 preferred-edge 切换的操作)
用可并二叉树实现 LCT:O(\log^2 V)
理论上讲,用任何一个可并平衡二叉树的结构维护每个链,等同于不用 Splay 而是其他的数据结构实现了 Link-Cut Tree。用 Splay 的好处在于它很随性,直接就等于实现了 LCT。
大神已经给我们证明了,如果用的不是 Splay,就是
二、斜二倍增在分层图中是可并的
斜二倍增简介及原文:https://www.luogu.com.cn/article/u81tks5o
随便在什么地方竖着切开,发现坏掉的东西的规模是
切断的时候,不需要修坏掉的后缀部分,有区间加法标记的需要在断开前处理和下放,可能还要让它能够知道断在哪里了,相当于要对坏掉的部分做一个切开的具体高度的标记。
这里
我们考虑这个问题:给一个斜二倍增的区间,切 k 次,最多可以弄坏几个地方。解应该是:最长的区间 1 次,次长的区间 2 次,4 次,依此类推,某个长度的区间数目大于 k 了,那就只能切坏 k 个。所以可以弄坏大约
综上,用斜二倍增优化 Dinic 算法
时间复杂度就是
实际上对数项
但是这个结构下次的
小优化?
p.s. Dinic 算法对于连接到汇的边好像是可以放松分层条件的。好像在后期放松了算法结束会快一点,在前期反而每轮变慢。如果放松了条件,寻找增广路之前预判一下到汇的连通性、去掉不连通的点和边好些。
每个点选择出边的时候,似乎用残量从大到小排序的顺序考虑(当前弧优化)流量增加速度会快,容易快点做完这一轮。
可以考虑先跑普通版的 Dinic 若干轮直到某轮的层数 L 满足
总结及其他
仔细回想一下,就是普通的 DFS 实现里面(非递归实现 DFS),突发奇想用斜二倍增去维护从源出发的容量、流量、最小边权,某条边满了切断、选择下一条边的时候,直接就用对数复杂度切开、接上了一个段(大家都设置了当前弧),而不是一层层向下走。实现起来非常的自然,竟然还有大神帮我们证明了拼接总次数是均摊
由于切开和接上的地方的深度是一致的,给人一种感觉是切开的时候什么都不需要标记,接上的时候自然就可以从最后一个结点开始向前跳跃,修好所有坏掉的地方,这样想是不对的,每个部分链,要严格记下链的终点、起点(接上的时候可能砍断了另一个链,需要找到起点,标记它新的终点)。
老年人没心思写代码,飘走。