记一类分治思想

这种分治思想大概是我在一年前看到的,但当时只看到了一道能用的题,就并没有当回事,最近又见到了可以用的题目,来写个随笔记一下。

不太清楚这个叫啥分治,要是有人在看而且知道可以跟我讲一下。

时间线段树(线段树分治)与这一思想较为类似,可以去看我的 时间线段树(线段树分治)学习笔记

问题一:给定一个 \(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\) 集合中的点的最短路,有转移方程:

\[{dp}_{S\cup\{w\},u,v}=\min\{{dp}_{S,u,v},{dp}_{S,u,w}+{dp}_{S,w,v}\} \]

然后按顺序将节点加入 \(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

posted @ 2022-06-08 17:26  rui_er  阅读(310)  评论(0编辑  收藏  举报