动态dp
动态dp
这个鬼玩意我已经点开-关上无数次了,当时dkw给洛谷贡模板题的时候还问过我一次来着......然而我并不会,然后,,,,然后NOIP就爆炸了。
所以,趁着难得滚到机房的时间,赶快学习一下QwQ。
直接搬洛谷上的模板题吧
题面
给定一棵个节点的树,点有点权,有次修改单点点权的操作,回答每次操作之后的最大独立集大小。
solution
首先有一个的傻逼做法,显然对于每次修改之后我们只需要的做一遍树就可以了。当然了,既然叫做动态dp,那么自然和dp逃不开关系了,所以我们还是简单的考虑一下这个转移。
设表示这个点不选时,子树中的最大独立集;表示这个点选时,子树中的最大独立集。那么转移很显然:,。其中是的儿子,是的点权。首先这个有一个弱智的加速方法,就是对于每一次修改的时候只修改这个点到根这一条链上的所有点就好了,然而随便把复杂度卡满。
类似的转移,我们这里的转移是和的形式,既然的和可以矩乘,那么这里也可以矩乘。(你把所有数取个相反数不就从变成了吗?)。说白点,就是原本矩阵转移是,将其变成就好了。
那么,在这道题目中,我们显然是一个一个子树的贡献逐个加入进当前节点的。我们先从比较容易的地方入手,先考虑一条链应该怎么做。显然一条链的转移很简单,即。那么把转移写成矩阵就是:
我知道这样子是假的矩乘啊,你把小点的矩阵自己补成的就好了
那么,对于一条链的时候,我们只需要用线段树维护中间的那个的矩阵就可以很容易的解决问题了。(如果这样子的话似乎的就可以拿到的分数了,突然觉得我自己菜爆了)
回到树上,考虑如何解决树上的问题。想清楚树和链的区别在哪里,显然,在于一个只有一个儿子,另外一个有多个儿子。考虑多个儿子如何合并答案,我们假装前面若干子树合并一起的结果是,并且中已经考虑完自身权值的贡献了。现在要合并进来的儿子是。那么我们考虑一下当前的转移是什么:。
然而,我们发现我们似乎没法让构成的矩阵变成的矩阵。
然而为啥我们要吊死在一棵树上啊(当然是在周围再找一棵树吊上去啊),我们把的值写成方程就好了啊。
很完美啊,虽然这玩意目前还是一个需要暴力修改到根节点的东西。
恩,暴力修改到根节点,我们怎么样才能让一个点到根节点的跳跃次数复杂度很对呢?——树链剖分啊。
似乎有点想法了,我们发现一个点的可以用它的所有儿子转移过来。而在进行修改操作的时候,我们只考虑等于重链条数的修改操作。想想这里要怎么实现:我们只修改重链条数次,意味着我们会在重链顶端修改重链顶端对于其父亲的贡献,但是修改当前位置的时候我们并没有顺势修改其重链上的父亲的值,那么我们显然就无法知道重链端点的值了。看起来似乎是这样一回事,仔细想想,重链是啥?是一条链。那么其重儿子的贡献可以单独用上面链的转移贡献进来,即用线段树维护每个节点的矩阵,这样子重儿子的值是可以很容易得到的。那么考虑当前重链端点对于上面父亲的贡献的时候就可以直接修改了。
总结一下的话,大致的过程是这样子的:首先我们考虑我们的转移方程,发现能够将其改写为矩乘的形式,那么我们首先将转移改为矩乘。我们把轻链和重链的转移分开考虑。这样子想,我们的重链被我们单独拎了出来,每个重链上都挂上了若干轻儿子,显然轻儿子对于重链上的独立集的选择是没有影响的,换而言之,如果轻儿子的贡献考虑完之后,那么等价于链上每个点选或者不选有一个权值,求最大独立集。而对于链上的这个东西,是可以直接用线段数维护矩阵支持修改和查询的,那么这题就只需要这么做就好了。即只修改这个点到达根节点上的所有轻链对于父亲的贡献,对于重儿子的贡献先暂时不考虑,每次线段树查询矩阵乘积即可求解出每个点的矩阵,就可以快速求解答案了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通