zoukankan      html  css  js  c++  java
  • 动态 DP 学习笔记

    问题引入

    • 给定一棵(n)个点的树,点带点权。有(m)次操作,每次操作给定(x,y),表示修改点(x)的权值为(y),你需要在每次操作之后求出这棵树的最大权独立集的权值大小。

    前置技能

    ​ 对矩阵乘法和重链剖分有一定了解即可。

    推导与解决

    ​ 首先我们这个题可以很简单写出一个方程,设(f[x][0])为不选(x)(x)子树内的最大独立集,设(f[x][1])为选(x)(x)子树内的最大独立集,那么有以下转移:

    [f[x][1]=val[x]+sum_{y in son[x]}f[y][0] ]

    [f[x][0] = sum_{yin son[x]}max(f[y][0], f[y][1]) ]

    ​ 我们转移到某一个儿子(y)的时候,那么有式子:

    [f[x][1]=S_1+f[y][0]\ f[x][0]=S_0+max(f[y][0], f[y][1]) ]

    (S_0,S_1)代表剩余的其他儿子的(dp)值之和,显然(,S_0,S_1)的计算与(y)无关,我们重新定义矩阵乘法(C=A*B)(C[i][j]=max_{k=1}^K(A[i][k]+B[k][j])),再把转移写成矩阵的形式:

    [(f[x][0],f[x][1])=(f[y][0],f[y][1])* left( egin{matrix} S_0 & S_1 \ S_0 & -inf end{matrix} ight) ]

    ​ 那么我们就可以通过矩阵动态维护(dp)了,我们首先进行树链剖分,令每个点的矩阵为从重儿子转移到自身的矩阵,那么我们每当对一个点进行修改的时候,它的实父亲的转移矩阵是不会有变化的,但是每条轻边会导致一个点的转移矩阵变化,那么只有(log n)个点的转移矩阵发生变化,那么我们可以用线段树快速查询出一个点的(dp)值,再用它去更新它虚父亲的转移矩阵即可。其中改变是这样的,定义转移后的(dp)值为(F),那么:

    [S_0=S_0 + max(F[y][1],F[y][0])-max(f[y][1],f[y][0])\S_1=S_1+F[y][0] -f[y][0] ]

    ​ 总结一下就是先记下当前重链顶端的(dp)值,再修改当前点的转移矩阵,然后就是求出当前重链顶端的(dp)值,再更新与这条重链通过轻边相连的上一条重链,这样子复杂度是(O(2^3nlog^2n))

    总结

    ​ 这个做法本质就是通过把转移写成矩阵乘法的形式再利用矩阵乘法的结合律可以快速算出一段区间的矩阵乘积,这类带修改的DP只要直接把转移写成矩阵形式维护矩阵就比较好做了。

  • 相关阅读:
    Swift8-枚举Enumerations
    Swift7-闭包
    Swift6-函数
    Swift5-控制流
    Swift4-集合类型
    什么是node.js
    nodejs的安装
    环境变量的认识,,,
    shell是什么,各种shell的初步认识,适用于初学者
    exports和module.exports的区别
  • 原文地址:https://www.cnblogs.com/brunch/p/10098167.html
Copyright © 2011-2022 走看看