动态DP主要用来解决对蒟蒻们(比如我)本来比较友好的树上DP问题(静态树),但是被一些毒瘤出题人(惊现lxl!)硬套上了对点权的修改操作,比如洛谷上的这道题(P4719 【模板】“动态 DP”&动态树分治):
给定一棵 n n n 个点的树,点带点权。有 m m m 次操作,每次操作给定 x , y x,y x,y,表示修改点 x x x 的权值为 y y y。你需要在每次操作之后求出这棵树的最大权独立集的权值大小。
首先概述一下独立集的概念:对于集合中的任意两元素 u , v u,v u,v ,满足: u u u 与 v v v 间没有边连接。
若忽略“动态”这一属性,即没有修改操作,则可以得到与Luogu P1352 没有上司的舞会类似的题意。
做法如下:
设函数 f ( x , y ) f(x,y) f(x,y) ( y ∈ { 0 , 1 } ) \Big(y\in\{0,1\}\Big) (y∈{0,1}) :
-
f ( x , 0 ) f(x,0) f(x,0) 表示以 x x x 为根的子树中,不取 x x x 时的最大权独立集的权值大小;
-
f ( x , 1 ) f(x,1) f(x,1) 表示以 x x x 为根的子树中,取 x x x 时的最大权独立集的权值大小。
则很容易推出:
f ( x , y ) = { ∑ p ∈ s o n x max ( f ( p , 1 ) , f ( p , 0 ) ) y = 0 ( ∑ p ∈ s o n x f ( p , 0 ) ) + v x y = 1 f(x,y)=\begin{cases}\sum_{p\in son_x} \max\Big(f(p,1),f(p,0)\Big)&y=0\\\Big(\sum_{p\in son_x}f(p,0)\Big)+v_x&y=1\end{cases} f(x,y)=⎩⎨⎧∑p∈sonxmax(f(p,1),f(p,0))(∑p∈sonxf(p,0))+vxy=0y=1
即意为:不若选 x x x , x x x 的孩子可以选也可以不选,可以贪心求最大值。
若选 x x x , x x x 的孩子绝对不选,别忘了加上本来的权值。
可以发现,若求 f ( x , y ) f(x,y) f(x,y) ,必须求出所有的 f ( p , y ) f(p,y) f(p,y) ( p ∈ s o n x ) \Big(p \in son_x \Big) (p∈sonx)
所以我们先递归达到底部,在回溯的过程中维护 d p dp dp 的值。
时间复杂度 O ( n ) O(n) O(n)
那么看看加上“动态“性质后的做法:
Solution 1 暴力
每次修改暴力重建 d p dp dp 值,时间复杂度 O ( n m ) O(nm) O(nm) 会炸。
Solution 2 暴力优化
每次修改影响到的 d p dp dp 值只在修改点 P P P 到根的路径上,复杂度 O ( ∑ l e n ) O(\sum len ) O(∑len)
可以发现,我们的瓶颈在于每次修改时都需要暴力遍历一遍,即使优化后若图是链状的,那你就优化了个寂寞。
那我们想要什么呢?我们想要 polylog !
那一般什么有 polylog 呢?明显是线段树/平衡树/二分。
那我比较菜只会线段树怎么办? 树链剖分!
子问题 solution
在搞树链剖分之前,我们先讨论一个子问题:
给你一个序列,每次对其中的单个数字进行修改,每次操作后在序列中选出若干个数,使其两两不相邻且权值最大,求出权值最大值。
朴素的方法:
设函数 f ( x , y ) f(x,y) f(x,y) ( y ∈ { 0 , 1 } ) \Big(y\in\{0,1\}\Big) (y∈{0,1}) :
-
f ( x , 0 ) f(x,0) f(x,0) 表示前 x x x 个数中不取 x x x 时的最大值
-
f ( x , 1 ) f(x,1) f(x,1) 表示前 x x x 个数中取 x x x 时的最大值
f ( x , y ) = { max ( f ( x − 1 , 1 ) , f ( x − 1 , 0 ) ) y = 0 f ( x − 1 , 0 ) + v x y = 1 f(x,y)=\begin{cases} \max\Big(f(x-1,1),f(x-1,0)\Big)&y=0\\f(x-1,0)+v_x&y=1\end{cases} f(x,y)={max(f(x−1,1),f(x−1,0))f(x−1,0)+vxy=0y=1
可以根据上文自行推导。
此时我们要想维护这样一个函数值,可以写成 ∣ f ( x , 0 ) f ( x , 1 ) ∣ \begin{vmatrix}f(x,0)\\f(x,1)\end{vmatrix} ∣∣∣∣f(x,0)f(x,1)∣∣∣∣
重定义矩阵乘法: A ∗ B = C C i , j = m a x ( A i , k + B k , j ) A*B=C \\C_{i,j}=max(A_{i,k}+B_{k,j}) A∗B=CCi,j=max(Ai,k+Bk,j)
那么把转移写成矩阵乘法,则为
∣ 0 0 v i − ∞ ∣ × ∣ f ( x − 1 , 0 ) f ( x − 1 , 1 ) ∣ = ∣ f ( x , 0 ) f ( x , 1 ) ∣ \begin{vmatrix}0&0\\v_i&-\infty\end{vmatrix}\times\begin{vmatrix}f(x-1,0)\\f(x-1,1)\end{vmatrix} =\begin{vmatrix}f(x,0)\\f(x,1)\end{vmatrix} ∣∣∣∣0vi0−∞∣∣∣∣×∣∣∣∣f(x−1,0)f(x−1,1)∣∣∣∣=∣∣∣∣f(x,0)f(x,1)∣∣∣∣
很容易发现,在知道 v i v_i vi 和 f ( x − 1 , 1 ) f(x-1,1) f(x−1,1) 与 f ( x − 1 , 0 ) f(x-1,0) f(x−1,0) 时, f ( x , 1 ) f(x,1) f(x,1) 与 f ( x , 0 ) f(x,0) f(x,0) 都是可求的
因为 m a x max max 具有结合律的,易证此矩阵乘法也有结合律。所以我们使用线段树维护,每次修改操作遍历到底就好了。
push_up 即为: a n s p = a n s l c p ∗ a n s r c p ans_p=ans_{lc_p}*ans_{rc_p} ansp=anslcp∗ansrcp
想要得到答案 query 根节点即可。
复杂度 O ( m log n ) O(m\log n) O(mlogn) ,欧耶 polylog !()
所以树链剖分能做什么呢?树链剖分能做的是把树划分成一个个这样的序列,只不过对每一个点还需要考虑一下其连接轻链的值。
为了降低复杂度,我们把当前线段树的根储存在链顶即可。
Solution 正解
设函数 f ( x , y ) f(x,y) f(x,y), g ( x , y ) g(x,y) g(x,y) ( y ∈ { 0 , 1 } ) \Big(y\in\{0,1\}\Big) (y∈{0,1}):
-
f ( x , 0 ) f(x,0) f(x,0) 表示以 x x x 为根的子树中,不取 x x x 时的最大权独立集的权值大小;
-
f ( x , 1 ) f(x,1) f(x,1) 表示以 x x x 为根的子树中,取 x x x 时的最大权独立集的权值大小。
-
g ( x , 0 ) g(x,0) g(x,0) 表示以 x x x 为根的轻链子树中,不取 x x x 时的最大权独立集的权值大小;
-
g ( x , 1 ) g(x,1) g(x,1) 表示以 x x x 为根的轻链子树中,取 x x x 时的最大权独立集的权值大小;
则很容易推出: g ( x , y ) = { ∑ p ∈ l i g h t s o n x max ( f ( p , 1 ) , f ( p , 0 ) ) y = 0 ( ∑ p ∈ l i g h t s o n x f ( p , 0 ) ) + v x y = 1 g(x,y)=\begin{cases}\sum_{p\in lightson_x} \max\Big(f(p,1),f(p,0)\Big)&y=0\\\Big(\sum_{p\in lightson_x}f(p,0)\Big)+v_x&y=1\end{cases} g(x,y)=⎩⎨⎧∑p∈lightsonxmax(f(p,1),f(p,0))(∑p∈lightsonxf(p,0))+vxy=0y=1
f ( x , y ) = { g ( x , 0 ) + max ( f ( h e a v y s o n x , 0 ) , f ( h e a v y s o n x , 1 ) ) y = 0 g ( x , 1 ) + f ( h e a v y s o n x , 0 ) y = 1 f(x,y)=\begin{cases}g(x,0)+\max\Big(f(heavyson_x,0),f(heavyson_x,1)\Big)&y=0\\g(x,1)+f(heavyson_x,0)&y=1\end{cases} f(x,y)={g(x,0)+max(f(heavysonx,0),f(heavysonx,1))g(x,1)+f(heavysonx,0)y=0y=1
那么我们可以推得新矩阵转移方程:
∣ g ( u , 0 ) g ( u , 0 ) g ( u , 1 ) − ∞ ∣ × ∣ f ( h e a v y s o n x , 0 ) f ( h e a v y s o n x , 1 ) ∣ = ∣ f ( x , 0 ) f ( x , 1 ) ∣ ( u ∈ l i g h t s o n x ) \begin{vmatrix}g(u,0)&g(u,0)\\g(u,1)&-\infty\end{vmatrix}\times\begin{vmatrix}f(heavyson_x,0)\\f(heavyson_x,1)\end{vmatrix} =\begin{vmatrix}f(x,0)\\f(x,1)\end{vmatrix}\bigg(u\in lightson_x\bigg) ∣∣∣∣g(u,0)g(u,1)g(u,0)−∞∣∣∣∣×∣∣∣∣f(heavysonx,0)f(heavysonx,1)∣∣∣∣=∣∣∣∣f(x,0)f(x,1)∣∣∣∣(u∈lightsonx)
先对树进行树链剖分,同时建里线段树维护 f ( i , j ) f(i,j) f(i,j)的值即可,线段树维护同上一样。
就不给贴代码了、