博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
动态 DP 学习笔记
阅读量:5123 次
发布时间:2019-06-13

本文共 1199 字,大约阅读时间需要 3 分钟。

问题引入

  • 给定一棵\(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_{y\in 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( \begin{matrix} S_0 & S_1 \\ S_0 & -\inf \end{matrix} \right) \]

​ 那么我们就可以通过矩阵动态维护\(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^3n\log^2n)\)

总结

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

转载于:https://www.cnblogs.com/brunch/p/10098167.html

你可能感兴趣的文章
存储(硬件方面的一些基本术语)
查看>>
观察者模式
查看>>
Weka中数据挖掘与机器学习系列之基本概念(三)
查看>>
Win磁盘MBR转换为GUID
查看>>
大家在做.NET B/S项目的时候多用什么设技术啊?
查看>>
Java SE和Java EE应用的性能调优
查看>>
Android设计模式系列--原型模式
查看>>
免费的论文查重网站
查看>>
C语言程序第一次作业
查看>>
leetcode-Sort List
查看>>
中文词频统计
查看>>
了解node.js
查看>>
想做移动开发,先看看别人怎么做
查看>>
Eclipse相关集锦
查看>>
虚拟化架构中小型机构通用虚拟化架构
查看>>
继承条款effecitve c++ 条款41-45
查看>>
Java泛型的基本使用
查看>>
1076 Wifi密码 (15 分)
查看>>
noip模拟赛 党
查看>>
bzoj2038 [2009国家集训队]小Z的袜子(hose)
查看>>