洛谷 P5291 [十二省联考2019]希望
洛谷 P5291 [十二省联考2019]希望
https://www.luogu.com.cn/problem/P5291
https://www.luogu.com.cn/problemnew/solution/P5291
一个有 \(n\) 个节点的树,一共有 \(k\) 个救援队,每个救援队有一个救援范围,即一个连通块 \(s_i\)
我们称一个节点 \(u\) 可被第 \(i\) 个救援队到达当且仅当
- \(u \in s_i\)
- \(\forall v \in s_i, dis(u, v) \le L\)
求有多少方案 \(\{ s_1, \cdots, s_k \}\) 满足存在一个节点可被所有救援队到达
两种方案不同当且仅当存在 \(i\) , 使得 \(s_i \not= s'_i\)
答案对 \(998244353\) 取模
\(1 \le n \le 10^6\)
\(0 \le L \le n\)
\(1 \le k \le 10\)
Tutorial
由于对于一种合法的方案,可以选择的点构成了一个连通块,我们可以利用树上 点数-边数=1 这一特点,将对于每个连通块统计答案转化为对于每个点和边统计答案.
设 \(f(u, i)\) 表示 \(u\) 的子树中包含 \(u\) 且所有点距离 \(u\) 不超过 \(i\) 的连通块个数 +1
设 \(g(u, i)\) 表示包含 \(u\) 且不包含 \(u\) 子树中的点且所有点距离 \(u\) 不超过 \(i\) 的连通块个数
每个点的贡献为
每条边的贡献为
直接DP的复杂度为 \(O(nL)\) ,接下来我们考虑如何优化计算 \(f,g\) 的过程
由于第二维与深度有关,所以考虑使用长链剖分优化.
\(f\) 的优化就是经典问题 , 需要进行所有值 +1 的操作,维护一个加法标记.
对于 \(g\) 我们需要从上到下更新,对于 \(u\) 节点,我们需要知道的只有 \(g(u, L)\) ,所以我们只需要保留 $g(u, L - len(u) + 1) $ 到 \(g(u, L)\) 的DP值,其中 \(len(u)\) 表示 \(u\) 出发的长链的点数.
对于上边 \(g\) 的转移中的 \(\prod\) 的部分,其实也就是 \(\dfrac {f(fa, i - 1) - 1}{f(v, i - 2)}\) 但是 \(f(v, i - 2)\) 可能等于 \(0\) ,我们可以看作 \(f(v, i - 2)\) 序列的一个前缀和一个后缀的乘积.
一个前缀的积可以表示为在计算 \(f\) 的过程中某一时刻的 \(f(fa, i - 1)\) ,为了得到这个值,我们可以在计算 \(f\) 每次转移时纪录修改过的值,就可以实现回溯.
一个后缀的积,可以在求 \(g\) 的时候采用与计算 \(f\) 时相反的顺序,类似维护 \(f\) 的方法维护.
注意由于 \(f,g\) 的定义中都有 不超过 ,以 \(f\) 为例, 设 \(l_u = len(u) - 1\),实际上 \(f(u, k), k > l_u\) 也是有值的,且等于 \(f(u, l_u)\) , 因此转移时 \(f(u)\) 的一段后缀会乘上 \(f(v, l_v)\)
当 \(f(v, l_v) = 0\) 时相当于将一段后缀赋值,可以维护 \(pos\) 表示 \(f(v, k), k \ge pos\) 的值都等于 \(0\)
当 \(f(v, l_v) \not=0\) 时,由于后缀之外的部分是 \(O(l_v)\) 的,所以我们可以将那段前缀乘上 \(f(v, l_v)\) 的逆元,然后维护一个乘法标记.
由于 \(n \le 10^6\) ,所以我们想办法去掉求逆元带来的 \(O(\log n)\) , 发现我们只需要求出所有 \(f(u, l_u)\) 即 \(u\) 子树中所有包含 \(u\) 的连通块的个数, 可以用类似求阶乘逆元的方法求出这 \(O(n)\) 个数的逆元.
总时间复杂度 \(O(n)\)
Summary
首先利用 点数-边数=1 将统计连通块转化为统计点.
写出DP方程后,发现第二维与深度有关,于是考虑长链剖分.
由于只关心 \(g(u, L)\) , 所以从上到下的部分也可以使用长链剖分
通过纪录每次修改的位置,来实现回溯.
对于全局加,后缀乘,后缀赋值,利用标记在复杂度不劣化的条件下维护.
求 \(n\) 个数逆元可以用类似求阶乘逆元的方法做到 \(O(n)\)