《解决树上连通块问题的一些技巧和工具》 学习笔记

参考 :\(2018\) 中国国家侯选队论文集。

\(1.\) 两类问题

计数问题 : 给出一棵树 \(T\) , 问多少联通块满足一个性质 \(A\)

最优化问题 : 给出一棵树和一个函数 , 求树 \(T\) 中所有满足某性质的联通块的最大收益。

\(2.\) 树上联通块的动态规划问题 :

朴素做法 :

对于一般的联通块问题 , 可以运用一个朴素做法 : 设 \(dp_{x}\) 表示在 \(x\) 的子树中选择一个包含 \(x\) 的联通块的最优答案 , 那么合并答案的时候只需要遍历子树枚举是否取就可以了。 如果合并儿子节点复杂度为 \(O(1)\) , 则总复杂度 \(O(N)\)。 如果合并儿子节点复杂度 \(O(size_{a} \cdot size_{b})\) , 那么总复杂度 \(O(N ^ 2)\)。 其理由在于每两个节点都会在其 \(LCA\) 产生 \(O(1)\) 的复杂度。

按照 \(DFS\) 序转移与点分治 :

对于一类树上联通块问题 , 如果合并信息不够高效 , 但加入单点的复杂度并不大 , 那么就可以按照 \(DFS\) 序加点 , 把合并子树变成添加单点。 这类问题中常见的有树上背包问题。 假设体积为 \(V\) , 那么就可以将 \(O(V ^ 2)\) 的合并复杂度优化至 \(O(V)\)。 具体方法如下:

先假定选出的联通块包含根节点 , 求出整棵树的 \(DFS\) 序 , 一个包含根的联通块必然是整棵树去掉若干个互不相交的子树。 换言之就是去掉了若干个 \(DFS\) 序列上的连续段。
\(dp_{i}\) 表示 \(DFS\) 序后 \(i\) 个节点的最优答案。 那么就有两种转移 :

\(1.\) 选择第 \(i\) 个节点 , 转移到了 \(dp_{i + 1}\)

\(2.\) 不选择第 \(i\) 个节点 , 转移到了 \(dp_{j}\) , 其中 \(j\) 为以 \(i\) 为根的子树的 \(DFS\) 序右端点 \(+1\)。表示去掉了整棵子树。

这样每次只是往背包中添加一个物品而非合并两个子树 , 故复杂度由 \(O(NM ^ 2)\) 优化至 \(O(NM)\)

而对于选出的联通块不包括根的情况 , 运用点分治 , 每个联通块递归做即可。

“点树 \(-\) 边数” :

对于一棵树 , 点树 \(-\) 边数恒为 \(1\)。 可以通过这个等式来统计某性质的联通块个数。记枚举每个点 / 每条边 , 用包含每个点的联通块个数减去包含每条边的联通块个数 , 这样每个联通块恰好被计算了一遍。 因此是正确的。

这个技巧常用于求若干个树上联通块交的方案数 , 因为若干个树上联通块的交集仍然是一个联通块。

线段树合并维护整体 \(DP\) :

有这样一类问题 : 每个点的 \(DP\) 数组维护 \(M\) 个值 , 合并两个子树时将对应位置合并 , 添加一个点时对某个位置进行修改。

考虑线段树合并 , 把每个点的 \(DP\) 值用一棵线段树合并 , 添加一个点时在线段树中修改 , 而合并子树时用线段树合并来批量完成转移。 线段树中插入总点数是不超过 \(O(NlogN)\) 的 , 而线段树合并复杂度不会超过其插入复杂度。

链分治维护动态 \(DP\) :

一个树上联通块必然是至多 \(O(logN)\) 条重链及其轻儿子的交集。

大体就是用动态 \(DP\) 里广义矩阵乘法的方法。

论文中的例题 :

\(1.\)

题解 :

来自 \(2018\) 年集训队互测。 作者是梁晏成。

能对一个连通块进行测试的点形成了一个树上联通块。 而若干个树上联通块的交同样是一个树上联通块。

考虑 “点减边” 容斥 , 即统计能被某个点 \(x\) 测试的完美集合个数 ,减去能同时被某条边两个端点测试的完美集合个数。

考虑计算能被点 \(x\) 测试的完美集合个数 , 不妨令 \(x\) 为树的根 , 去除不满足条件中的点 , 接着 , 进行树形 \(DP\) , 这相当于问树上有多少联通块满足其重量和不超过 \(M\) 且价值和最大。 这是一个经典的最优化背包问题。 只要在背包记录最优值的同时再记录达到最优值的方案数就可以了。 注意到合并儿子节点的复杂度是 \(O(M ^ 2)\) 的。 但用 \(DFS\) 序优化就可以做到 \(O(M)\)

本题还涉及一个组合数取模的问题 : 首先发现模数是 \(5 ^ {23}\)。 而组合数取模有一个多项式做法 :

显然 , 可以维护出 \(N!\) , \(K!\) , \((N - K)!\) 中非 \(5\) 倍数的乘积和因子 \(5\) 的个数 , 然后合并即可。

\(5\) 倍数的乘积可用多项式 \(f_{n}(x) = \prod_{i \mod 5 \neq 0}{(x + i)}\) 的常数项得到。

因为 \(f_{10k}(x) = f_{5k}(5k + x)\) , 因为模数是 \(5 ^ {23}\) , 故多项式只需保留 \(23\) 项即可。在合并的时候可以暴力卷积。

对于 \(5\) 的倍数 , 可以在 \(O(log(N))\) 的时间内计算得到。

时间复杂度 : \(O(N ^ 2M)\)

\(2.\)

题解 :

来自清华大学冬令营 \(2018\)

\(f(x , c)\) 表示在 \(x\) 的子树中选一个联通块 , 两种(一种)颜色分别为 \(col_{x}\)\(c\) 的方案数。

枚举儿子节点 \(y\) , 如果 \(y\)\(x\) 的颜色相同 , 那么直接将 \(f(y , ?) + 1\) 乘给 \(f(x , ?)\) , 否则将 \(f(y , col_{x}) + 1\) 乘给 \(f(x , col_{y})\)

第二种转移总共只会用到 \(O(N)\) 个状态 , 而对于第一种转移线段树合并即可。

时间复杂度 : \(O(NlogN)\)

补充题目 :

\(NOI2020\) 一试 \(B\)

线段树合并即可。 之前写过题解 , 故不再赘述。

时间复杂度 : \(O(Nlog ^ 2N)\)

\(LOJ3053\)

\(2019\) 年十二省联考压轴题。

容易 ,中心能够存在的位置是树上的一个连通块 ,一个方案合法当且仅当该联通块大小非零。

考虑"点减边"容斥 , 答案即为 \(\sum{f(x) ^ k - g(x) ^ k}\)

不妨进行树形 \(DP\) , 记 \(f_{i , x}\) 表示以 \(i\) 为根的子树中 , 包含 \(i\) , 且距离 \(i\) 不超过 \(x\) 的联通块数量。

同理再对边进行一遍 \(DP\) 即可。

这样就可以拿到约 \(60\) 分左右的高分。

满分做法还需要再这个做法的基础上用长链剖分和可回退化数据结构优化做到 \(O(N)\) , 比较复杂 , 不赘述了。

posted @ 2020-10-16 17:44  evenbao  阅读(1591)  评论(0编辑  收藏  举报