动态 dp
没写代码,暂且不知道有多少处笔误,还需要好好理解。。
动态 dp
矩阵乘法大家都会!dp 大家都会!线段树大家都会!
一些线性 dp 可以写成矩阵乘法的形式,这里矩阵乘法可能是 \((+,\times)\),也可能是 \((\max,+)\),也可能是 \((\min,+)\) 等等,但是只要有结合律就可以。
在每个点处的转移都写成一个矩阵的形式,而要询问单独拿出来一段区间 dp 的结果,就是询问这个区间的矩阵乘积。
基于这个原理,可以用数据结构来维护区间矩阵乘法的结果。这个就是动态 dp。
一句话:将 dp 转移写成矩阵乘法,然后用数据结构维护矩阵乘积。
例题
给定 01 串,支持单点修改,区间询问单独把这个 01 串拿出来后,每次可以 \(+2^i\) 或者 \(-2^i\),最少几次把其消为 \(0\).
线段树维护一个分治信息,大概维护一个前面有没有给自己一个进位 \(0/1\),会不会给后面一个进位 \(0/1\),最小代价是多少。
可以用矩阵解释,这个就是动态 dp?
P4719 "动态 DP"&动态树分治
序列上的带修最大权独立集我们都会动态 dp,现在最大权独立集上树了。
考虑从序列到树的常见套路是静态LCT重链剖分,对于一条重链直接维护一个动态 dp 的矩阵,但是轻儿子的贡献怎么算?
设 \(g_{x,0/1}\) 为 \(x\) 节点不选/选的,并且不考虑重儿子时的 dp 值是多少。
对于一条重链,如果每个节点的 \(g\) 都维护出来了,那么这条重链上的动态 dp 就能维护出正确的答案。
考虑到最大权独立集具体的式子,一个儿子 dp 值改变之后,对父亲的 \(g\) 值的修改可以直接 \(\mathcal{O}(1)\) 加减出来,那么树剖修改的时候跳重链的时候,在父亲的重链的线段树上单点修改把父亲的 \(g\) 值改掉就可以了。
时间复杂度大概是 \(\mathcal{O}(n\log^2n)\),再带一个矩阵乘法的复杂度。
游戏
树上每个节点有若干个石子,两个人轮流在树上取石子,先手可以任选一个位置开始取一个石子,接下来每个人取石子的节点必须和对方上次取石子的节点相邻。单点修改,每次修改求谁必胜。
首先先先考虑没有修改的情况:
考虑给每个树上每个石子一个节点,如果两个石子在树上相邻就连一条边。树是二分图,所以这样建图出来也是一个二分图,那么这就是一个二分图博弈,后手必胜当且仅当这个二分图有完美匹配。(为什么?)
考虑这个匹配可以直接贪心匹配,每次尽可能的让叶子往上配对,然后将叶子删掉。
于是这个等价于对于每个点记录一个 \(f_i\) 表示以 \(i\) 为根的子树,奇数层 \(-\) 偶数层的石子数,那么就有一个 \(f_u=a_u-\sum_{v\in son(u)}f_v\).
那么存在一个匹配就当且仅当所以 \(f\) 非负并且 \(f(1)=0\).
树剖维护 \(f\),奇偶层分开用线段树维护即可。
保卫王国
大家都知道 最小权点覆盖集 \(=\) 所有点的权值之和 \(-\) 最大权独立集。
也就是两者互为补集。
强制一个点必须选,就把它权值设为 \(0\),最后给最小权点覆盖集加上原先的权值即可;如果一个点必须不选,就把它权值设为 \(\infty\).
这样就变成了动态 dp 模板了,总权值减去最大权独立集,当然直接推最小权点覆盖的动态 dp 也可以。
ZJOI2019 Minimax 搜索
对于一个答案 \(k\),最优的策略就是与 \(W\) LCA 深度为奇数的的 \(+k\),偶数的 \(W\) 的 \(-k\).这样就知道每个叶子应该是 \(+k\) 还是 \(-k\) 了。
考虑计算答案 \(\leq k\) 的方案数,差分一下即为所求。
那么考虑 \(f_{i,0/1/2}\) 表示 \(i\) 子树内,\(i\) 节点权值变 \(>W\) /变 \(<W\) /变 \(=W\) 的集合数。注意到这个 \(f\) 总和是 \(2^c\),\(c\) 是叶子个数,所以可以只记录其中两个即可。但是每次都用给 \(2^c\) 转移起来太麻烦了,直接考虑将方案数改成概率。
那么记 \(f_{i}\) 为 \(i\) 子树内使得 \(i\) 权值变 \(<W\) 的概率,\(g_i\) 表示 \(i\) 子树内使得 \(i\) 权值变 \(>W\) 的概率。
那么每次 \(k\gets k+1\) 的时候均摊至多会有两个叶子的 \(f\) 和 \(g\) 会发生改变。
动态 dp 维护即可,有亿、小细节。