给你一棵 n 个点的树,点带权,对于每个节点求出距离它不超过 k 的所有节点权值和 mi。
第一行两个正整数 n,k。
接下来 n−1 行,每行两个正整数 u,v,表示 u,v 之间有一条边。
最后 n 行,每行一个非负整数 ci,表示点权。
输出格式
* Lines 1..N: Line i should contain the value of M(i).
输出 n 行,第 i 行一个整数表示 mi。
输入输出样例
输入 #1复制
6 2 5 1 3 6 2 4 2 1 3 2 1 2 3 4 5 6
输出 #1复制
15 21 16 10 8 11
说明/提示
There are 6 fields, with trails connecting (5,1), (3,6), (2,4), (2,1), and (3,2). Field i has C(i) = i cows.
Field 1 has M(1) = 15 cows within a distance of 2 trails, etc.
【数据范围】
对于 100%100% 的数据:1≤n≤105,1≤k≤20,0≤ci≤1000
解析:
第一次dfs,先从根节点为1时,开始遍历的的子树,记录它的子树。
第二次dfs,进行根的转换,记录其总和。
利用容斥原理,避免多次相加同一个数,这时的 dp2[v][j] += dp2[x][j-1] - dp1[v][j-2]; 的含义就出来了,dp2[v][i]因为根节点处理好了,所以直接处理子节点。利用根节点转换。
dp2[x][j-1] 表示的是因为x 距离子节点v为1距离,所以我们应该得到的是以x为中心,距离为小于等于 j-1的那些点。
dp1[v][j-2]是以v为中心距离,以v为根,距离为j-2那些点,最后删去,得到的就是以v为中心距离为小于等于 j 的哪些点
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n,val[N],k;
vector<int> g[N];
int dp1[N][22],dp2[N][22];
void dfs1(int x,int fa)
{
for(int i = 0;i <= k;i++)
{// dp1[i][j] 表示 在以 i为 根 ,距离不超过 j 的 总和
dp1[x][i] = val[x];
}
for(int i =0;i < g[x].size();i++)
{
int v = g[x][i];
if(v != fa)
{
dfs1(v,x);
for(int j = 1;j <= k;j++)
{
dp1[x][j] += dp1[v][j-1];// 只是记录下面节点的
}
}
}
return;
}
void dfs2(int x,int fa)
{
for(int i= 0;i < g[x].size();i++)
{
int v = g[x][i];
if(v!= fa)
{
dp2[v][1] += dp1[x][0];
for(int j = 2;j <= k;j++)
{
dp2[v][j] += dp2[x][j-1] - dp1[v][j-2];// dp1 是以 v为根距离为 j-2的那些点
// dp2[x][j-1] 减去根节点的差距
}
dfs2(v,x);
}
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i = 1;i < n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
for(int i = 1;i <= n;i++)
{
scanf("%d",&val[i]);
}
dfs1(1,0);
for(int i = 1;i <= n;i++)
{
for(int j = 0;j <= k;j++)
{
dp2[i][j] = dp1[i][j];
}
}
dfs2(1,0);
for(int i = 1;i <= n;i++)
{
cout << dp2[i][k]<<'\n';
}
return 0;
}
时间复杂度为:O(n*k);
总结:
在遍历 每颗树形结构是,可以先从1到 叶子节点 ,是dfs后在处理父节点信息。
在判断其父节点和子树直接的关系时,是父节点处理儿子节点,在进行dfs()下个儿子节点 遍历。