[ZJOI2019] Minimax搜索 题解
感觉是道好题,我们先稍微理一下思路。
首先,我们定义在初始状态下,根节点的权值为 \(W\)。
先考虑一个差分的技巧,即我们对于每个 \(k\),求出 \(w(S) \le k\),\(S\) 的个数,然后 \(k\) 的答案就是用 \(\le k\) 的个数减去 \(\le k-1\) 的个数。
我们先假设我们此时通过枚举知道了 \(w(S)\le k\) 所对应的 \(k\),考虑怎么去算 \(S\) 的个数。
我们发现一个比较 \(\texttt{naive}\) 的 DP,(为什么我感觉一点也不 \(\texttt{naive}\),根本就没想到),即定义 \(dp_{x,0/1/2}\) 分别表示通过操作 \(x\) 子树内的叶子节点,有多少种 \(S\) 可以分别满足 \(x\) 的当前权值 \(<W,=W,>W\)。
初始值就是叶子节点 \(x\),\(x+k,x-k\) 与 \(W\) 的关系,这里就不细说了。
转移应该比较简单,分讨一下当前节点的奇数和偶数深度即可。答案就是 \(dp_{1,0}+dp_{1,2}\)。
但是你会发现这样会算重,就是说有的 \(S\) 它不仅可以做到 \(<W\),也可以做到 \(>W\),而它会被计算两次。所以这样做是不行的。
考虑有没有什么办法能够做到只考虑 \(>W,<W\) 中的一个便可以得到答案。
我们思考一下根节点这个 \(W\) 的值是怎么来的,显然是通过从 \(W\to 1\) 这条链上一步一步过来的,并且显然这条链是对根节点的修改起决定性因素的,故我们考虑去重点观察一下这条链。
我们发现这条链上只要有一个点的权值发生了变动那么根节点的权值就会发生改变。
更具体的,如果我们将这条链上的边拆开,这样这棵树就会变成若干个连通块,每个连通块的根节点就是这条链上的节点。我们只需要求出每个连通块根节点改变的概率即可。(其实也就是方案)
考虑一个事情,对于连通块一个点 \(x\),若其深度为奇数,那么显然我们将其改为 \(>W\) 那么根节点的权值就会发生改变,若为偶数,就改为 \(<W\)。并且显然你符合这样改的方案数一定是合法的全部方案数。
故我们定义 \(dp_x\) 表示将 \(x\) 的权值变为 \(>W\)(如果 \(x\) 所在连通块的根节点的深度是奇数就是 \(>W\),否则就是 \(<W\))有多少种方案。虽然 \(<W,>W\) 的定义不同,但是转移其实是是差不多的:
-
\(x\) 的深度为奇数,小小的容斥转移,\(dp_x=2^{siz_x}-\prod_{j\in son_x}(2^{siz_j}-dp_j)\)
-
\(x\) 的深度为偶数,\(dp_x=\prod_{j\in son_x}dp_j\)
最终答案就是对于每个连通块的根节点 \(x\),有 \(2^n-\prod dp_x\)
对于 \(dp_x\) 的初始值,就是对于每个叶子,是否满足 \(x-k<W\) 或者 \(x-k>W\) (这个是其所在联通块的根节点深度的奇偶性所决定的符号)满足就是 \(1\) 否则就是 \(0\)。
此时对于每一个 \(k\) 我们就得到了求解的方式。发现对于 \(k\) 从 \(1\to n\),每个叶子节点的 \(dp_x\) 只会从 \(0\to 1,1\to 0\) 最多改变一次,此时直接通过动态 DP 暴力修改叶子节点然后求解即可。
采用最普通的树剖求解复杂度为 \(\mathcal{O}(n\log ^2n)\)。
代码好难写,先提供一份暴力作为对该思路的参考和理解。正解直接套动态 DP,因为作者时间紧张,所以省略了。正解代码见此博客。
当然中间有一些细节,就是你动态 DP 在修改的时候要抵消之前的贡献,而这里面的贡献可能有 \(0\),不能直接使用逆元,需要记录当前 \(dp_x\) 的子节点中有多少个 \(dp_j\) 为 \(0\),其他的更多直接套 DDP 板子就行了。