很经典很好的一个树形dp
很明显dp[i][j]表示距离i在范围j以内的权值和
我们还很容易想到维护一个deep[i][j]数组
表示i的子树中距离i在范围j以内的权值和
这个题难就难在距离i在范围j以内的点可能是在i的上头
这时候转移方程就要考虑率容斥一下
对于(u,v)这条边
dp[u][kk]=dp[v][kk-1]-deep[u][kk-2]+deep[u][kk-1];
这个转移方程真的绝绝子,以后很多题目都可以联想到这个方法
还有很多细节在下面代码里面,要想完整写出来确实很有难度
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=1e5+5;
int val[maxn];
vector<int>Q[maxn];
int n,k;
int d[maxn][25],dp[maxn][25];
void dfs(int u,int fa,int kk){
d[u][kk]+=val[u];//首先要加上他本身
for(int i=0;i<Q[u].size();i++){
int to=Q[u][i];
if(to==fa)continue;
dfs(to,u,kk);//由子节点更新而来,所以先遍历子节点
d[u][kk]+=d[to][kk-1];
}
}
//dp[v][kk]=dp[fa[v]][kk-1]-d[v][kk-2]+d[v][kk]
void calc(int u,int fa,int kk){
//因为由父亲转移过来,先转移再calc
if(kk>=2)
dp[u][kk]=dp[fa][kk-1]-d[u][kk-2]+d[u][kk];
else
dp[u][kk]=dp[fa][kk-1]+d[u][kk];//在kk<2的情况下,就压根不需要容斥了,dp[fa][kk-1]就没有包括d[u][kk-2]
for(int i=0;i<Q[u].size();i++){
int to=Q[u][i];
if(to==fa)continue;
calc(to,u,kk);
}
}
int main(){
scanf("%d%d",&n,&k);
for(int a,b,i=1;i<=n-1;i++){
scanf("%d%d",&a,&b);
Q[a].push_back(b);
Q[b].push_back(a);
}
for(int i=1;i<=n;i++)
scanf("%d",&val[i]),dp[i][0]=d[i][0]=val[i];
for(int i=1;i<=k;i++)
dfs(1,1,i),dp[1][i]=d[1][i];//设置初始根节点
for(int kk=1;kk<=k;kk++)
for(int i=0;i<Q[1].size();i++)//要越过根节点再进行dp,因为根节点没有父亲没法转移,并且根节点的状态我们已经初始化了
calc(Q[1][i],1,kk);
for(int i=1;i<=n;i++)printf("%d\n",dp[i][k]);
return 0;
}