BZOJ_2591
由于K比较小,我们可以用f[i][j]表示距离第i个节点为j的所有节点的权值和,那么最后实际上就是对于特定的i输出sum{f[i][j]}。
对于一个特定的f[i][j]来讲,一共由两部分构成,一部分路径来自于子节点,一部分路径来自于父节点,我们可以先从叶子节点开始向上dp,将来自于子节点的部分计算出来,再从根节点开始向下dp,将来自于父节点的部分累加上。
#include<stdio.h> #include<string.h> #define MAXD 100010 #define MAXM 200010 int N, K, first[MAXD], e, next[MAXM], v[MAXM], c[MAXD]; int f[MAXD][25], dp[MAXD][25]; void add(int x, int y) { v[e] = y; next[e] = first[x], first[x] = e ++; } void init() { memset(first, -1, sizeof(first[0]) * (N + 1)), e = 0; for(int i = 1; i < N; i ++) { int x, y; scanf("%d%d", &x, &y); add(x, y), add(y, x); } for(int i = 1; i <= N; i ++) scanf("%d", &c[i]); } int q[MAXD], fa[MAXD]; void solve() { int rear = 0; q[rear ++] = 1, fa[1] = -1; for(int i = 0; i < rear; i ++) { int x = q[i]; for(int j = first[x]; j != -1; j = next[j]) if(v[j] != fa[x]) fa[v[j]] = x, q[rear ++] = v[j]; } for(int i = rear - 1; i >= 0; i --) { int x = q[i]; memset(f[x], 0, sizeof(f[x][0]) * (K + 1)); for(int j = first[x]; j != -1; j = next[j]) if(v[j] != fa[x]) for(int k = 1; k <= K; k ++) f[x][k] += f[v[j]][k - 1]; f[x][0] = c[x]; } for(int i = 0; i < rear; i ++) { int x = q[i]; for(int j = first[x]; j != -1; j = next[j]) if(v[j] != fa[x]) { int y = v[j]; for(int k = K; k >= 1; k --) f[y][k] += f[x][k - 1] - (k > 1 ? f[y][k - 2] : 0); } } for(int i = 1; i <= N; i ++) { int ans = 0; for(int j = 0; j <= K; j ++) ans += f[i][j]; printf("%d\n", ans); } } int main() { while(scanf("%d%d", &N, &K) == 2) { init(); solve(); } return 0; }