【题目】NowCoder-1100B 乃爱与城市拥挤程度

树形DP解题技巧
本文探讨了一种利用树形动态规划(DP)解决特定树结构问题的方法,具体讲解了如何计算树中每个节点距离不超过给定值的节点数量及权值乘积,通过详细的状态转移方程和实例解析,帮助读者深入理解树形DP的应用。
题目大意

给出一棵nnn个结点的树以及一个常数kkk,对于树中的每个结点vvv,求出:

  1. 到点vvv的距离不超过kkk的点的个数。
  2. 若点xxx距离点vvv不超过kkk,则v,xv,xv,x路径上的所有点的权值加111。求距离vvv不超过kkk的点的权值的乘积。答案对109+710^9+7109+7取模。

n⩽105n\leqslant 10^5n105k⩽10k\leqslant 10k10


思路

应该能看出是树形DP。
首先要考虑到贡献的计算。在询问某个点uuu的时候,对于uuu本身,其子树内外的贡献都要计算;对于uuu下方的点,只计算其子树内的贡献;对于uuu子树外的点,只计算其子树外的贡献。因此DP状态分子树内外两种情况讨论。

讨论的时候用(u,v)(u,v)(u,v)表示一条父子边,其中uuu是父亲。

先解决第一个问题。

f[u][i][1]f[u][i][1]f[u][i][1]表示点uuu子树内距离uuu不超过iii的点的个数(包括点uuu本身),f[u][i][0]f[u][i][0]f[u][i][0]表示点uuu子树外距离uuu不超过iii的点的个数(不包括点uuu本身)。
对于f[u][i][1]f[u][i][1]f[u][i][1],既然到uuu的距离为iii,那么到uuu的某个儿子vvv的距离一定是i−1i-1i1,可以累计儿子的贡献,再加上uuu本身,得到第一个方程:
f[u][i][1]=(∑f[v][i−1][1])+1f[u][i][1]=\bigg(\sum f[v][i-1][1]\bigg)+1f[u][i][1]=(f[v][i1][1])+1

对于f[v][i][0]f[v][i][0]f[v][i][0],子树外的点到父亲uuu的距离也一定是i−1i-1i1,并且既可以来自uuu的子树外,也可以来自uuu的其它儿子。既然是其它儿子,就要减去uuu这棵子树的贡献,得到第二个方程:
f[v][i][0]=f[u][i−1][0]+f[u][i−1][1]−f[v][i−2][1]f[v][i][0]=f[u][i-1][0]+f[u][i-1][1]-f[v][i-2][1]f[v][i][0]=f[u][i1][0]+f[u][i1][1]f[v][i2][1]

最终答案为每个结点子树内外的贡献之和,由于结点本身的贡献只在子树内被计算,不需要去重。不难发现这个答案就是第二个问题中的点权。
ans1[u]=f[u][k][0]+f[u][k][1]ans_1[u]=f[u][k][0]+f[u][k][1]ans1[u]=f[u][k][0]+f[u][k][1]

然后是第二个问题。

g[u][i][1]g[u][i][1]g[u][i][1]表示点uuu子树内距离uuu不超过iii的点(包括点uuu本身)在到达点uuu的路径上对经过的点的权值加111后,点uuu子树内距离uuu不超过iii的点的权值的乘积。g[u][i][0]g[u][i][0]g[u][i][0]的含义同理,就不费口舌叙述了。

对于g[u][i][1]g[u][i][1]g[u][i][1],即点uuu子树内的点,同样可以累计儿子的贡献,再乘上点uuu本身的权值。点uuu子树内能到达点uuu的点一共有f[u][i][1]f[u][i][1]f[u][i][1]个,每一个点都会对点uuu的权值加111,因此点uuu的权值就为f[u][i][1]f[u][i][1]f[u][i][1],得到第三个方程:
g[u][i][1]=(∏g[v][i−1][1])⋅f[u][i][1]g[u][i][1]=\bigg(\prod g[v][i-1][1]\bigg)\cdot f[u][i][1]g[u][i][1]=(g[v][i1][1])f[u][i][1]

对于g[v][i][0]g[v][i][0]g[v][i][0],即点vvv子树外的点,同样可以先到父亲uuu,累计uuu的贡献,再除去vvv这棵子树的贡献,得到第四个方程:
g[v][i][0]=g[u][i−1][0]⋅g[u]][i−1][1]g[v][i−2][1]g[v][i][0]=\frac{g[u][i-1][0]\cdot g[u]][i-1][1]}{g[v][i-2][1]}g[v][i][0]=g[v][i2][1]g[u][i1][0]g[u]][i1][1]

如果你列出的方程是上面这个样子,你就完了。
第四个方程有非常严重的错误。我们试图用g[u][i−1][0]⋅g[u][i−1][1]g[u][i-1][0]\cdot g[u][i-1][1]g[u][i1][0]g[u][i1][1]计算所有距离uuu不超过i−1i-1i1的点到达点uuu后产生的点权的乘积,显然子树内外的贡献都要计算。但此时点uuu的点权被拆成了两部分:来自子树内的f[u][i−1][1]f[u][i-1][1]f[u][i1][1]和来自子树外的f[u][i−1][0]f[u][i-1][0]f[u][i1][0]。首先g[u][i−1][0]g[u][i-1][0]g[u][i1][0]根本就不包含点uuu来自子树外的贡献f[u][i−1][0]f[u][i-1][0]f[u][i1][0];即使g[u][i−1][0]g[u][i-1][0]g[u][i1][0]包含了f[u][i−1][0]f[u][i-1][0]f[u][i1][0],上面的方程也是把点uuu的两部分权值相乘,但是显然这两部分应该是相加的关系。(不清楚的可以手动模拟)
于是这个方程中点uuu的权值是错误的。由于我们知道这个错误的权值是f[u][i−1][1]f[u][i-1][1]f[u][i1][1],可以把这个错误的权值除掉,把正确的权值乘上去,注意仍然要减去点vvv这棵子树的贡献。
g[v][i][0]=g[u][i−1][0]⋅g[u]][i−1][1]g[v][i−2][1]⋅f[u][i−1][0]+f[u][i−1][1]−f[v][i−2][1]f[u][i−1][1]=g[u][i−1][0]⋅g[u]][i−1][1]g[v][i−2][1]⋅f[v][i][0]f[u][i−1][1]\begin{aligned} g[v][i][0]&=\frac{g[u][i-1][0]\cdot g[u]][i-1][1]}{g[v][i-2][1]}\cdot\frac{f[u][i-1][0]+f[u][i-1][1]-f[v][i-2][1]}{f[u][i-1][1]}\\ &=\frac{g[u][i-1][0]\cdot g[u]][i-1][1]}{g[v][i-2][1]}\cdot\frac{f[v][i][0]}{f[u][i-1][1]} \end{aligned}g[v][i][0]=g[v][i2][1]g[u][i1][0]g[u]][i1][1]f[u][i1][1]f[u][i1][0]+f[u][i1][1]f[v][i2][1]=g[v][i2][1]g[u][i1][0]g[u]][i1][1]f[u][i1][1]f[v][i][0]

同理,最终统计答案的时候也不能直接把子树内外的贡献相乘了,还是需要先把错误的权值除掉,乘上正确的权值。
ans2[u]=g[u][k][0]⋅g[u][k][1]⋅f[u][i−1][0]+f[u][i−1][1]f[u][i−1][1]ans_2[u]=g[u][k][0]\cdot g[u][k][1]\cdot\frac{f[u][i-1][0]+f[u][i-1][1]}{f[u][i-1][1]}ans2[u]=g[u][k][0]g[u][k][1]f[u][i1][1]f[u][i1][0]+f[u][i1][1]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值