【十二省联考2019】希望(容斥,换根DP,长链剖分,懒标记)

暴力的想法是考虑钦定每个点作为到达点并统计贡献,但显然这样会算重。

注意到会算重的到达点一定构成了一个连通块,这是一个很好的性质,方便了我们容斥:我们直接用点的贡献减去边的贡献(一条边的两个端点同时是到达点)即可,因为一个连通块满足点数减边数等于 \(1\)

先考虑点的贡献,需要统计以某个点为根且深度不超过 \(L\) 的连通块个数。

可以换根 DP:设 \(f_{u,i}\) 表示 \(u\) 子树内以 \(u\) 为根且深度不超过 \(i\) 的连通块个数,\(g_{u,i}\) 表示 \(u\) 子树外以 \(u\) 为根且深度不超过 \(i\) 的连通块个数,转移:(其中 \(s_u\) 表示 \(u\) 的儿子集合)

\[\begin{aligned} f_{u,i}&=\prod_{v\in s_u} (f_{v,i-1}+1)\\ g_{u,i}&=g_{fa,i-1}\prod_{v\in s_{fa},v\neq u}(f_{v,i-2}+1)+1 \end{aligned} \]

注意 \(\prod\limits_{v\in s_{fa},v\neq u}(f_{v,i-2}+1)\) 不能写成 \(\dfrac{f_{fa,i-1}}{f_{u,i-2}+1}\),因为在模意义下有可能 \(f_{u,i-2}+1\)\(0\)

单点贡献即为 \((f_{u,L}\cdot g_{u,L})^k\)。同时单边 \((fa,u)\) 的贡献也得到了,即为 \((f_{u,L-1}\cdot g_{u,L})^k\)

然而暴力 DP 的复杂度为 \(O(nL)\) 的,需要优化。

先来处理 \(f\),看到 \(f\) 的定义和深度有关,考虑用长链剖分优化:记 \(son_u\)\(u\) 的重儿子,让 \(f_{u}\) 继承 \(f_{son_u}\) 的信息,再从 \(u\) 的其他儿子 \(v\) 转移过来。这里下标偏移一位的问题可以用指针处理,然后全局+1可以用 add 标记处理。

但注意到 \(f_{v}\) 的数组大小范围并不是 \(d_v\)(设 \(d_v\) 表示 \(v\) 子树内深度最大值),而是 \(0\sim L\) 都有值(根据 \(f\) 的定义)。

\(f_v\) 也是有特征的:\(f_{v,d_v}=f_{v,d_v+1}=\cdots =f_{v,L}\)。于是用 \(f_v\)\(f_u\) 转移时,实际上做的是前 \(d_v+1\) 位暴力转移,后面的位做的是同乘 \(f_{v,d_v}+1\)

这可以用线段树维护,但是有一个更加美妙的做法:

  • \(f_{v,d_v}+1=0\),则 \(f_u\)\(d_v+1\) 位之后将一直会是 \(0\),这可以打个 tag。
  • \(f_{v,d_v}+1\neq 0\),我们打一个 \(f_u\) 全局乘 \(f_{v,d_v}+1\) 的 tag,然后前 \(d_v+1\) 位暴力转移时再乘上 \(f_{v,d_v}+1\) 的逆。

然后就会出现很多 tag 要维护……

具体来说有以下五种:全局乘 \(mul\)\(mul\) 的逆元 \(imul\),全局加 \(add\)(优先级在全局乘后),重置位置 \(lim\)\(lim\) 以后的位置都被重置),重置值 \(reset\)(受全局乘和全局加影响)。

此时 \(f\) 就大致维护好了,时间复杂度 \(O(n)\)

接下来是如何维护 \(g\)

发现我们只需要维护 \(g_{u,L-d_u}\sim g_{u,L}\) 的值,因为求 \(g_{u,L}\) 只需要用到 \(g_{fa,L-1}\) 的值。

那么同样考虑用长链剖分优化:让 \(g_{son_u}\) 继承 \(g_u\) 的信息,而其他儿子由 \(g_u\) 暴力转移。

现在问题是对每一个儿子 \(v\) 求出 \(\prod\limits_{v'\in s_{u},v'\neq v}(f_{v',i-2}+1)\)

显然是需要前缀和后缀拼起来的,我们这么考虑:我们让求 \(f\) 的时候按 \(d\) 从大到小枚举儿子(这样第一个枚举的一定是重儿子),并记录 \(f\) 的变化,然后求 \(g\) 的时候按 \(d\) 从小到大枚举儿子,前缀显然可以边枚举边求出,而后缀则可以通过不断还原 \(f\) 求出。

\(g\) 也可以用 5 个 tag 维护。

有关求逆:我们需要每次 \(O(1)\) 计算出 \(f_{u,d_u}\) 的逆,注意到此时第二维是没有影响的,所以可以先求出所有的 \(f_{u,d_u}\) 再离线 \(O(n)\) 预处理它们的逆元(每个值的逆=全部乘起来的逆×前缀积×后缀积)。

代码没写完。

posted @ 2022-10-31 08:15  ez_lcw  阅读(30)  评论(0编辑  收藏  举报