一、题目描述:

  给你一棵 n 个点的树,点带权,对于每个节点,求出距离它不超过 k 的所有节点权值和。n<=1e5,k<=20。


 二、解题思路:

  这题一开始想了一个 O(knlogn) 的线段树合并,写了一半感觉不好转移,最后写了十几分钟的 dp 写出来了。( dp代码就是短 )

  两遍 dfs 。第一遍统计从儿子到父亲,第二遍统计从父亲到儿子,注意一些细节即可,不多讲。时间复杂度 O(kn)。


  、完整代码:

 1 #include<iostream>
 2 #include<cstring>
 3 #define N 100010
 4 using namespace std;
 5 int n,k,u1,v1;
 6 int c[N],ans[N],f[N][25];
 7 struct EDGE{
 8     int v,nxt;
 9 }edge[N*2];
10 int head[N],cnt;
11 void add(int u,int v)
12 {
13     edge[++cnt].v=v;
14     edge[cnt].nxt=head[u];
15     head[u]=cnt;
16 }
17 void dfs1(int now,int ff)
18 {
19     f[now][0]+=c[now];
20     for(int i=head[now];i!=-1;i=edge[i].nxt)
21         if(edge[i].v!=ff)
22         {
23             dfs1(edge[i].v,now);
24             for(int j=1;j<=k;j++)
25                 f[now][j]+=f[edge[i].v][j-1];
26         }
27 }
28 void dfs2(int now,int ff)
29 {
30     for(int i=head[now];i!=-1;i=edge[i].nxt)
31         if(edge[i].v!=ff)
32         {
33             for(int j=k;j>=2;j--)
34                 f[edge[i].v][j]-=f[edge[i].v][j-2];
35             //这里注意倒序枚举 
36             for(int j=2;j<=k;j++)
37                 f[edge[i].v][j]+=f[now][j-1];
38             f[edge[i].v][1]+=c[now];
39             dfs2(edge[i].v,now);
40         }
41 }
42 int main()
43 {
44     ios::sync_with_stdio(false);
45     cin.tie(0);cout.tie(0);
46     cin>>n>>k;
47     memset(head,-1,sizeof(head));
48     for(int i=1;i<n;i++)
49     {
50         cin>>u1>>v1;
51         add(u1,v1),add(v1,u1);
52     }
53     for(int i=1;i<=n;i++)
54         cin>>c[i];
55     dfs1(1,0);dfs2(1,0);
56     for(int i=1;i<=n;i++)
57         for(int j=0;j<=k;j++)
58             ans[i]+=f[i][j];
59     for(int i=1;i<=n;i++)
60         cout<<ans[i]<<'\n';
61     return 0;
62 }

四、做题心得:

  感觉这题真没啥难度,不过做了一道蓝题还是挺高兴的啦。拜拜!

posted on 2023-04-05 21:19  trh0630  阅读(127)  评论(0)    收藏  举报