记一类分治思想
这种分治思想大概是我在一年前看到的,但当时只看到了一道能用的题,就并没有当回事,最近又见到了可以用的题目,来写个随笔记一下。
不太清楚这个叫啥分治,要是有人在看而且知道可以跟我讲一下。
时间线段树(线段树分治)与这一思想较为类似,可以去看我的 时间线段树(线段树分治)学习笔记 。
问题一:给定一个 \(n\) 点 \(m\) 边有向图,对于所有 \(1\le u,v,w\le n\),求 \(u\) 到 \(v\) 且不经过 \(w\) 的最短路长度之和。
这是我最早见到的这个思想的题目,虽然没找到题在哪里。
容易发现暴力 Floyd 一遍是 \(\mathcal O(n^4)\) 的,能不能想一个更快的算法呢?是有的。
考虑 Floyd 算法的最一般形式的由来,设 \({dp}_{S,u,v}\) 表示 \(u\) 到 \(v\) 只能经过 \(S\) 集合中的点的最短路,有转移方程:
然后按顺序将节点加入 \(S\) 之后滚掉 \(S\) 这一维就是 Floyd 算法。
注意到节点加入 \(S\) 的顺序其实是不重要的,发现在刚刚的 \(\mathcal O(n^4)\) 流程中没有充分利用这一点,进行了很多重复计算。考虑这样一种分治算法:
FLOYD(int[][]& dp, int l, int r) # 在 dp 数组的基础上,再添加 [l, r] 的点到点集 S 中
for k in [l, r] do
for i in [1, n] do
for j in [1, n] do
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j])
DIVIDE_AND_CONQUER(int[][] dp, int l, int r) # 计算不加入 [l, r] 范围的点的全源最短路
result = 0
if l == r do
for u in [1, n] do
for v in [1, n] do
result += dp[u][v]
else do
mid = (l + r) / 2
dp_new = dp
FLOYD(dp_new, mid + 1, r)
result += DIVIDE_AND_CONQUER(dp_new, l, mid)
dp_new = dp
FLOYD(dp_new, l, mid)
result += DIVIDE_AND_CONQUER(dp_new, mid + 1, r)
return result
这种算法的正确性显然,时间复杂度可以写作 \(T(n)=2T\left(\dfrac{n}{2}\right)+\mathcal O(n\cdot N^2)\),用主定理可知为 \(\mathcal O(N^3\log N)\)。
观察以上过程,可以简单地总结一下这类分治算法的流程:
DIVIDE_AND_CONQUER(l, r)
:要求解 \([l, r]\) 范围的答案。- 如果 \(l = r\),对这个点求解即可。
- 否则取中点 \(mid\) 进行分治:
- 添加右半边对左半边的贡献,递归左半边统计答案。
- 将右半边的贡献擦除,添加左半边对右半边的贡献,递归右半边统计答案。
- 如果需要,将两部分答案合并。
总结:这类分治算法通过求解多个点的答案时共用信息的方式,大大减少了计算量,通常可以把一个暴力计算的 \(\mathcal O(n)\) 降低到 \(\mathcal O(\operatorname{polylog} n)\)。
问题二(CF1681F):有一棵 \(n\) 个点的树,每条边有颜色,对于每条路径 \(u\to v\),求路径上恰好出现一次的颜色数量的和。
本题有一个 \(\mathcal O(n)\) 做法,是类似虚树的思想,对每个颜色分开统计答案,但这不在我们讨论的范畴。
我是从 tourist 大神的 代码 里再次看到的这个思想。
不难发现,对于颜色 \(c\) 的答案即为把所有颜色 \(c\) 的边删掉后,每条 \(c\) 边连接的两个连通块大小之积的和。
接着利用上面的思想,DIVIDE_AND_CONQUER(l, r)
即为要求解 \(c\in [l, r]\) 范围的答案。
使用可撤销并查集维护连通块,对于分治中心 \(mid\),我们把右半边的边连接的连通块合并,然后统计左半边答案,之后执行撤销操作,把左半边的边连接的连通块合并,然后统计右半边答案。对于单点的求解,枚举颜色 \(c\) 的边,求两个连通块大小之积即可。
时间复杂度为 \(\mathcal O(n\log^2 n)\)。
问题三(P4141):背包,所有少一个物品的情况的最大价值之和。
有 \(\mathcal O(nm)\) 做法,不过由上面的分治,可以有 \(\mathcal O(nm\log n)\) 做法。
其他应用:“线段树分治”(时间轴线段树)的思想比较类似,只是统计贡献的方式略有不同。
问题四(ABC279E)