do_while_true

一言(ヒトコト)

动态 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 维护即可,有亿、小细节。

posted @ 2022-07-27 11:28  do_while_true  阅读(264)  评论(0编辑  收藏  举报