浅谈动态dp

动态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})

  1. f ( x , 0 ) f(x,0) f(x,0) 表示以 x x x 为根的子树中,不取 x x x 时的最大权独立集的权值大小;

  2. 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)=psonxmax(f(p,1),f(p,0))(psonxf(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) (psonx)

所以我们先递归达到底部,在回溯的过程中维护 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})

  1. f ( x , 0 ) f(x,0) f(x,0) 表示前 x x x 个数中不取 x x x 时的最大值

  2. 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(x1,1),f(x1,0))f(x1,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}) AB=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(x1,0)f(x1,1)=f(x,0)f(x,1)

很容易发现,在知道 v i v_i vi f ( x − 1 , 1 ) f(x-1,1) f(x1,1) f ( x − 1 , 0 ) f(x-1,0) f(x1,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=anslcpansrcp

想要得到答案 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})

  1. f ( x , 0 ) f(x,0) f(x,0) 表示以 x x x 为根的子树中,不取 x x x 时的最大权独立集的权值大小;

  2. f ( x , 1 ) f(x,1) f(x,1) 表示以 x x x 为根的子树中,取 x x x 时的最大权独立集的权值大小。

  3. g ( x , 0 ) g(x,0) g(x,0) 表示以 x x x 为根的轻链子树中,不取 x x x 时的最大权独立集的权值大小;

  4. 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)=plightsonxmax(f(p,1),f(p,0))(plightsonxf(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)(ulightsonx)

先对树进行树链剖分,同时建里线段树维护 f ( i , j ) f(i,j) f(i,j)的值即可,线段树维护同上一样。

就不给贴代码了、

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值