经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
树形DP + 换根DP
来源:cnblogs  作者:SunnyYuan  时间:2023/7/28 10:17:19  对本文有异议

树形DP——基础

P1352 没有上司的舞会

\(f[i][0/1]\) 表示第 \(i\) 个人不去或者去。

如果第 \(i\) 个人没去,那么下属可去可不去,所以 \(f[i][0] = \sum max\{f[j][0],f[j][1]\}\)\(j\)\(i\) 的子节点。

如果第 \(i\) 个人去了,那么下属不能去,所以 \(f[i][1] = a[i] + \sum f[j][0]\)\(j\)\(i\) 的子节点。

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. #include <cmath>
  5. using namespace std;
  6. const int N = 6010;
  7. struct Edge {
  8. int to, next;
  9. }e[N * 2];
  10. int head[N], idx;
  11. void add(int a, int b) {
  12. idx++, e[idx].to = b, e[idx].next = head[a], head[a] = idx;
  13. }
  14. int n;
  15. int a[N];
  16. int f[N][2];
  17. void dfs(int u, int fa) {
  18. f[u][1] = a[u];
  19. for (int i = head[u]; i; i = e[i].next) {
  20. int to = e[i].to;
  21. if (to == fa) continue;
  22. dfs(to, u);
  23. f[u][1] += f[to][0];
  24. f[u][0] += max(f[to][0], f[to][1]);
  25. }
  26. }
  27. int main() {
  28. ios::sync_with_stdio(false);
  29. cin.tie(nullptr);
  30. cin >> n;
  31. for (int i = 1; i <= n; i++) cin >> a[i];
  32. for (int i = 1; i < n; i++) {
  33. int a, b;
  34. cin >> a >> b;
  35. add(a, b);
  36. add(b, a);
  37. }
  38. dfs(1, 0);
  39. cout << max(f[1][0], f[1][1]) << '\n';
  40. return 0;
  41. }

POJ 1463 / UVA1292 Strategic game

与上一题类似,

\(f[i][0/1]\) 表示第 \(i\) 个人是否驻守。

如果第 \(i\) 个人没去,那么它的儿子必须驻守,所以 \(f[i][0] = 1 + \sum f[j][1]\)\(j\)\(i\) 的子节点。

如果第 \(i\) 个人去了,那么它的儿子可去可不去,所以 \(f[i][1] = \sum min\{f[j][0],f[j][1]\}\)\(j\)\(i\) 的子节点。

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N = 1510;
  6. struct Edge {
  7. int to, next;
  8. }e[N * 2];
  9. int head[N], idx;
  10. void add(int a, int b) {
  11. idx++, e[idx].to = b, e[idx].next = head[a], head[a] = idx;
  12. }
  13. int n;
  14. int f[N][2];
  15. void dfs(int u, int fa) {
  16. f[u][1] = 1;
  17. for (int i = head[u]; i; i = e[i].next) {
  18. int to = e[i].to;
  19. if (to == fa) continue;
  20. dfs(to, u);
  21. f[u][0] += f[to][1];
  22. f[u][1] += min(f[to][0], f[to][1]);
  23. }
  24. }
  25. void solve() {
  26. memset(f, 0, sizeof(f));
  27. memset(head, 0, sizeof(head));
  28. idx = 0;
  29. for (int i = 1; i <= n; i++) {
  30. int a, num;
  31. scanf("%d:(%d)", &a, &num);
  32. a++;
  33. for (int j = 1; j <= num; j++) {
  34. int b;
  35. scanf("%d", &b);
  36. b++;
  37. add(a, b);
  38. add(b, a);
  39. }
  40. }
  41. dfs(1, 0);
  42. printf("%d\n", min(f[1][0], f[1][1]));
  43. }
  44. int main() {
  45. while (~scanf("%d", &n)) solve();
  46. return 0;
  47. }

P3574 [POI2014] FAR-FarmCraft

\(f[i]\) 表示\(i\) 为根节点的子树中(包括节点 \(i\))的所有人安装好游戏所需要的时间(与下面的 \(g[i]\) 并没有包含关系,管理员也没有强制性要求要回到根节点,比如会出现下图情况)。

\(g[i]\) 表示从 \(i\) 开始往下走,兜一圈又回到 \(i\) 所需要的时间。

实际上 \(f[i]\) 可能 \(< g[i]\),比如当出现如下情况的时候:

image

那我们先访问那个节点呢?

分为两种情况考虑,即 \(f[i] - g[i] \geq 0\)\(f[i] - g[i] < 0\) 两种情况。

如果管理员回到了起点那些人还没有装完(即 \(f[i] - g[i] \geq 0\)),那么就需要等待 \(f[i] - g[i]\) 的时间所有人才能安装好。

根据常识,在等待的这段时间我们可以去下一家,以减少所需的总时间。

这里我们利用贪心,让需要等待时间最久的作为第一个访问的节点,

这样可以管理员在他漫长的安装时间内将电脑送给其他人。

而如果出现了像上图一样的情况(即 \(f[i] - g[i] < 0\)) 的情况,

根本就不需要等待,

也就不用排序,

随机访问即可,

但为了简单起见,

排了序也没有什么问题。

所以我们可以对 \(f[i] - g[i]\) 从大到小进行排序。

再挨个访问即可。

然后就是利用 \(f\)\(g\) 来用子树信息更新父亲节点。

如下图:

image

先说结论:只安装到 \(i\) 点会需要 \(\sum (g[j] + 2) + 1 + f[i]\) 的时间能完成安装,其中 \(j\) 为比 \(i\) 先遍历到的同一层的节点(如上图)。

为什么是这样呢?

第一部分的 \(\sum (g[j] + 2)\) 表示遍历完所有 \(j\) 子树的节点,每次都回到根节点(所以要 \(+2\))。

第二部分的 \(+1\) 表示从根节点走到 \(i\) 所需要的步骤(即为 \(1\) 步)。

最后一部分的 \(f[i]\) 表示把 \(i\) 子树内所有的游戏装好了需要花的时间。

总时间取 \(\max\) 即可, 即 \(f[root] = \max\{\sum (g[j] + 2) + f[i] + 1\}\)

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. #include <vector>
  5. using namespace std;
  6. const int N = 500010;
  7. struct Edge {
  8. int to, next;
  9. }e[N * 2];
  10. int head[N], idx;
  11. void add(int a, int b) {
  12. idx++;
  13. e[idx].to = b;
  14. e[idx].next = head[a];
  15. head[a] = idx;
  16. }
  17. int n, t[N];
  18. int f[N], g[N];
  19. void dfs(int u, int fa) {
  20. vector<int> wait;
  21. for (int i = head[u]; i; i = e[i].next) {
  22. int to = e[i].to;
  23. if (to == fa) continue;
  24. dfs(to, u);
  25. wait.push_back(to);
  26. }
  27. sort(wait.begin(), wait.end(), [](const int& a, const int& b) { return f[a] - g[a] > f[b] - g[b]; });
  28. for (int i = 0; i < wait.size(); i++) {
  29. f[u] = max(f[u], g[u] + 1 + f[wait[i]]);
  30. g[u] += g[wait[i]] + 2;
  31. }
  32. if (t[u] > g[u] && u != 1) f[u] = max(f[u], t[u]);
  33. }
  34. int main() {
  35. ios::sync_with_stdio(false);
  36. cin.tie(nullptr);
  37. cin >> n;
  38. for (int i = 1; i <= n; i++) cin >> t[i];
  39. for (int i = 1; i < n; i++) {
  40. int a, b;
  41. cin >> a >> b;
  42. add(a, b);
  43. add(b, a);
  44. }
  45. dfs(1, 0);
  46. cout << max(f[1], g[1] + t[1]) << '\n';
  47. return 0;
  48. }

树形DP——树上背包

P2014 CTSC1997 选课

不难发现这是个树结构。

课程相当于树上的每一个节点,

学分相当于权值。

\(f[i][j][k]\) 表示遍历到第 \(i\) 棵子树的第 \(j\) 个元素并且选择 \(k\) 个节点可以得到的最大权值。

\(f[cur][i][j] = \max \{f[cur][i - 1][j - l] + f[ver[cur][i]][size[ver[i]]][l]\}\)

其中 \(ver[i][j]\) 表示 \(i\) 节点的第 \(j\) 个子节点,\(size[x]\) 表示以 \(x\) 为根节点的子树的大小。

然后像01背包一样把第二维滚掉就可以了。

C++代码

版本1:

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N = 310;
  6. struct Edge {
  7. int to, next;
  8. }e[N * 2];
  9. int head[N], idx;
  10. void add(int a, int b) {
  11. idx++;
  12. e[idx].to = b;
  13. e[idx].next = head[a];
  14. head[a] = idx;
  15. }
  16. int n, m;
  17. int sz[N];
  18. int a[N];
  19. int f[N][N];
  20. void dfs(int u) {
  21. sz[u] = 1;
  22. f[u][1] = a[u];
  23. for (int i = head[u]; i; i = e[i].next) {
  24. int to = e[i].to;
  25. dfs(to);
  26. sz[u] += sz[to];
  27. for (int j = min(sz[u], m + 1); j >= 1; j--) {
  28. for (int k = 1; k <= min(sz[to], j - 1); k++) {
  29. f[u][j] = max(f[u][j], f[u][j - k] + f[to][k]);
  30. }
  31. }
  32. }
  33. }
  34. int main() {
  35. ios::sync_with_stdio(false);
  36. cin.tie(nullptr);
  37. cin >> n >> m;
  38. for (int i = 1; i <= n; i++) {
  39. int b;
  40. cin >> b >> a[i];
  41. add(b, i);
  42. }
  43. dfs(0);
  44. cout << f[0][m + 1] << '\n';
  45. return 0;
  46. }

版本2:

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N = 310;
  6. struct Edge {
  7. int to, next;
  8. }e[N * 2];
  9. int head[N], idx;
  10. void add(int a, int b) {
  11. idx++;
  12. e[idx].to = b;
  13. e[idx].next = head[a];
  14. head[a] = idx;
  15. }
  16. int n, m;
  17. int sz[N];
  18. int a[N];
  19. int f[N][N];
  20. void dfs(int u) {
  21. sz[u] = 1;
  22. f[u][1] = a[u];
  23. for (int i = head[u]; i; i = e[i].next) {
  24. int to = e[i].to;
  25. dfs(to);
  26. for (int j = min(sz[u], m + 1); j >= 1; j--) {
  27. for (int k = 1; k <= sz[to] && j + k <= m + 1; k++) {
  28. f[u][j + k] = max(f[u][j + k], f[u][j] + f[to][k]);
  29. }
  30. }
  31. sz[u] += sz[to];
  32. }
  33. }
  34. int main() {
  35. ios::sync_with_stdio(false);
  36. cin.tie(nullptr);
  37. cin >> n >> m;
  38. for (int i = 1; i <= n; i++) {
  39. int b;
  40. cin >> b >> a[i];
  41. add(b, i);
  42. }
  43. dfs(0);
  44. cout << f[0][m + 1] << '\n';
  45. return 0;
  46. }

P4516 [JSOI2018] 潜入行动

\(f[i][j][0/1][0/1]\) 表示在以 \(i\) 为根节点的子树中除节点 \(i\) 的全部节点被覆盖而且使用了 \(j\) 个监听设备时节点 \(i\) 的状况,第一个 \([0/1]\) 表示有没有放置监听设备,第二个 \([0/1]\) 表示 \(i\) 有没有被覆盖。

有:

\(f[u][i + j][0][0] = \sum f[u][i][0][0] \times f[v][j][0][1]\)

\(f[u][i + j][0][1] = \sum f[u][i][0][1] \times (f[v][j][0][1] + f[v][j][1][1]) + f[u][i][0][0] \times f[v][j][1][1]\)

\(f[u][i + j][1][0] = \sum f[u][i][1][0] \times (f[v][j][0][0] + f[v][j][0][1])\)

\(f[u][i + j][1][1] = \sum f[u][i][1][1] \times (f[v][j][0][0] + f[v][j][0][1] + f[v][j][1][0] + f[v][j][1][1]) + f[u][i][1][0] \times (f[v][j][1][0] + f[v][j][1][1])\)

注意:

1. 每次计算时都要把 \(f[u]\) 清空重新统计,所以只能用 \(tmp\) 数组当一下“替身 ”了。*

2. 此题卡空间、时间。

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <vector>
  6. using namespace std;
  7. const int N = 100010, M = 110, mod = 1000000007;
  8. struct Edge {
  9. int to, next;
  10. }e[N * 2];
  11. int head[N], idx;
  12. void add(int a, int b) {
  13. idx++;
  14. e[idx].to = b;
  15. e[idx].next = head[a];
  16. head[a] = idx;
  17. }
  18. struct num {
  19. int x;
  20. num operator+(num b) {
  21. while (b.x >= mod) b.x -= mod;
  22. long long tmp = 1ll * x + b.x;
  23. while (tmp >= mod) tmp -= mod;
  24. num res;
  25. res.x = tmp;
  26. return res;
  27. }
  28. void operator+=(num b) {
  29. while (b.x >= mod) b.x -= mod;
  30. long long tmp = 1ll * x + b.x;
  31. while (tmp >= mod) tmp -= mod;
  32. x = tmp;
  33. }
  34. num operator*(num b) {
  35. num res;
  36. res.x = (1ll * x * b.x) % mod;
  37. return res;
  38. }
  39. void operator=(int k) {
  40. x = k;
  41. }
  42. };
  43. int n, k;
  44. int sz[N];
  45. num f[N][M][2][2];
  46. num t[M][2][2];
  47. void dfs(int u, int fa) {
  48. sz[u] = 1;
  49. f[u][0][0][0] = 1;
  50. f[u][1][1][0] = 1;
  51. for (int tmp = head[u]; tmp; tmp = e[tmp].next) {
  52. int v = e[tmp].to;
  53. if (v == fa) continue;
  54. dfs(v, u);
  55. memcpy(t, f[u], sizeof(t));
  56. memset(f[u], 0, sizeof(f[u]));
  57. for (int i = min(sz[u], k); i >= 0; i--) {
  58. for (int j = 0; j <= min(sz[v], k - i); j++) {
  59. f[u][i + j][0][0] += t[i][0][0] * f[v][j][0][1];
  60. f[u][i + j][0][1] += t[i][0][1] * (f[v][j][0][1] + f[v][j][1][1]) + t[i][0][0] * f[v][j][1][1];
  61. f[u][i + j][1][0] += t[i][1][0] * (f[v][j][0][0] + f[v][j][0][1]);
  62. f[u][i + j][1][1] += t[i][1][1] * (f[v][j][0][0] + f[v][j][0][1] + f[v][j][1][0] + f[v][j][1][1]) + t[i][1][0] * (f[v][j][1][0] + f[v][j][1][1]);
  63. }
  64. }
  65. sz[u] += sz[v];
  66. }
  67. }
  68. int main() {
  69. ios::sync_with_stdio(false);
  70. cin.tie(nullptr);
  71. cin >> n >> k;
  72. for (int i = 1; i < n; i++) {
  73. int x, y;
  74. cin >> x >> y;
  75. add(x, y);
  76. add(y, x);
  77. }
  78. dfs(1, 0);
  79. cout << (f[1][k][0][1] + f[1][k][1][1]).x << '\n';
  80. return 0;
  81. }

换根DP

换根法思路:

  1. 自下而上递推;
  2. 自上而下递推。

P3478 [POI2008] STA-Station

题目描述:

image

思路:

个节点 \(u\) 为根的子树大小 \(s[u]\)

然后我们设 \(f[i]\) 为以 \(i\) 为根时所有节点的深度之和,\(j\)\(i\) 的子节点。

那么对于所有 \(j\) 的子节点,深度都减 \(1\),所以总共减少了 \(s[j]\)

对于所有不是 \(j\) 的子节点的节点,深度都加 \(1\) ,所以总共加了 \(n - s[j]\)

所以 \(f[j] = f[i] - s[j] + n - s[j] = f[i] + n - 2 \times s[j]\)

最后取 \(\max\) 即可。

代码:

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. using i64 = long long;
  6. const int N = 1000010;
  7. struct Edge {
  8. int to, next;
  9. }e[N * 2];
  10. int head[N], idx;
  11. void add(int a, int b) {
  12. idx++;
  13. e[idx].to = b;
  14. e[idx].next = head[a];
  15. head[a] = idx;
  16. }
  17. int n;
  18. int sz[N];
  19. void dfs1(int u, int fa) {
  20. sz[u] = 1;
  21. for (int i = head[u]; i; i = e[i].next) {
  22. int to = e[i].to;
  23. if (to == fa) continue;
  24. dfs1(to, u);
  25. sz[u] += sz[to];
  26. }
  27. }
  28. int f[N];
  29. int maxv, ans;
  30. void dfs2(int u, int fa) {
  31. for (int i = head[u]; i; i = e[i].next) {
  32. int to = e[i].to;
  33. if (to == fa) continue;
  34. f[to] = f[u] + n - 2 * sz[to];
  35. dfs2(to, u);
  36. }
  37. }
  38. int main() {
  39. ios::sync_with_stdio(false);
  40. cin.tie(nullptr);
  41. cin >> n;
  42. for (int i = 1; i < n; i++) {
  43. int x, y;
  44. cin >> x >> y;
  45. add(x, y);
  46. add(y, x);
  47. }
  48. dfs1(1, 0);
  49. for (int i = 1; i <= n; i++) f[1] += sz[i];
  50. dfs2(1, 0);
  51. for (int i = 1; i <= n; i++) if (f[i] > maxv) maxv = f[i], ans = i;
  52. cout << ans << '\n';
  53. return 0;
  54. }

AcWing 287. 积蓄程度 / POJ 3585 Accumulation Degree

题目描述:

image

思路:

\(d[i]\) 表示 \(i\) 点可以向下传递的最大流量。

\(f[i]\) 表示 \(i\) 点可以向外传递的最大流量(包括向下流的流量和向上流的流量)。

\(\text{dfs}\) 一遍求出 \(d\)

有:

如无特殊说明 \(to\) 表示 \(i\) 的子节点,\(edge(i, to)\) 表示 \(i\)\(to\) 这条边的最大流量。

\(d[i] = \sum \min(d[to], edge(i, to))\)

\(f[to] = \min(edge(i, to), f[i] - \min(edge(i, to), d[to]))\)

同时注意如果根的度为 \(1\)(如下图) ,那么计算它的子节点时要采用公式:

\(f[i] = f[to] + edge(i, to)\)

image

代码:

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N = 200010, INF = 0x3f3f3f3f;
  6. struct Edge {
  7. int to;
  8. int next;
  9. int w;
  10. }e[N * 2];
  11. int head[N], idx, deg[N];
  12. void add(int a, int b, int c) {
  13. idx++;
  14. e[idx].to = b;
  15. e[idx].next = head[a];
  16. e[idx].w = c;
  17. head[a] = idx;
  18. }
  19. int n;
  20. int f[N], d[N];
  21. int dfs1(int u, int fa) {
  22. for (int i = head[u]; i; i = e[i].next) {
  23. int to = e[i].to;
  24. if (to == fa) continue;
  25. d[u] += min(dfs1(to, u), e[i].w);
  26. }
  27. if (deg[u] == 1) return INF;
  28. return d[u];
  29. }
  30. void dfs2(int u, int fa) {
  31. for (int i = head[u]; i; i = e[i].next) {
  32. int to = e[i].to;
  33. if (to == fa) continue;
  34. if (deg[u] == 1) f[to] = d[to] + e[i].w;
  35. else f[to] = d[to] + min(e[i].w, f[u] - min(d[to], e[i].w));
  36. dfs2(to, u);
  37. }
  38. }
  39. void solve() {
  40. idx = 0;
  41. memset(head, 0, sizeof(head));
  42. memset(deg, 0, sizeof(deg));
  43. memset(f, 0, sizeof(f));
  44. memset(d, 0, sizeof(d));
  45. cin >> n;
  46. for (int i = 1; i < n; i++) {
  47. int x, y, z;
  48. cin >> x >> y >> z;
  49. add(x, y, z);
  50. add(y, x, z);
  51. deg[x]++;
  52. deg[y]++;
  53. }
  54. dfs1(1, 0);
  55. f[1] = d[1];
  56. dfs2(1, 0);
  57. int ans = 0;
  58. for (int i = 1; i <= n; i++) ans = max(ans, f[i]);
  59. cout << ans << '\n';
  60. }
  61. int main() {
  62. ios::sync_with_stdio(false);
  63. cin.tie(nullptr);
  64. int T;
  65. cin >> T;
  66. while (T--) solve();
  67. return 0;
  68. }

P2986 [USACO10MAR] Great Cow Gathering G

题目描述:

image

思路:

\(f[u]\) 表示如果选择集会的地点为 \(u\) 点时所需时间。

根据经验,我们可以得到

\(f[to] = f[u] - to\text{节点下所有奶牛个数} \times w(u, to) + (奶牛总数 - to\text{节点下所有奶牛个数}) \times w(u, to)\)

代码:

  1. #include <bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. const int N = 100010, M = 200010;
  5. struct Edge {
  6. int to;
  7. int next;
  8. int w;
  9. }e[M];
  10. int head[N], idx;
  11. void add(int a, int b, int c) {
  12. idx++;
  13. e[idx].to = b;
  14. e[idx].next = head[a];
  15. e[idx].w = c;
  16. head[a] = idx;
  17. }
  18. int sz[N];
  19. int cnt[N];
  20. int dfs(int u, int fa) {
  21. int all = 0;
  22. sz[u] = cnt[u];
  23. for (int i = head[u]; i; i = e[i].next) {
  24. int to = e[i].to;
  25. if (to == fa) continue;
  26. all += dfs(to, u);
  27. sz[u] += sz[to];
  28. all += sz[to] * e[i].w;
  29. }
  30. return all;
  31. }
  32. int f[N], n, get_n;
  33. void dfs2(int u, int fa) {
  34. for (int i = head[u]; i; i = e[i].next) {
  35. int to = e[i].to;
  36. if (to == fa) continue;
  37. f[to] = f[u] - sz[to] * e[i].w + (get_n - sz[to]) * e[i].w;
  38. dfs2(to, u);
  39. }
  40. }
  41. signed main() {
  42. ios::sync_with_stdio(false);
  43. cin.tie(nullptr);
  44. cin >> n;
  45. for (int i = 1; i <= n; i++) cin >> cnt[i], get_n += cnt[i];
  46. for (int i = 1; i < n; i++) {
  47. int x, y, z;
  48. cin >> x >> y >> z;
  49. add(x, y, z);
  50. add(y, x, z);
  51. }
  52. f[1] = dfs(1, 0);
  53. dfs2(1, 0);
  54. int min_sum = 0x3f3f3f3f3f3f3f3f;
  55. for (int i = 1; i <= n; i++) {
  56. if (min_sum > f[i]) {
  57. min_sum = f[i];
  58. // ans = i;
  59. }
  60. }
  61. cout << min_sum << '\n';
  62. return 0;
  63. }

P3047 [USACO12FEB]Nearby Cows G

题目描述

image

思路

使用换根DP,

\(dp[i][j]\) 表示以 \(i\) 为根节点的子树中深度小于等于 \(j\) 的点的权值之和。

\(f[i][j]\) 表示将第 \(i\) 个点作为整棵树的根节点深度小于等于 \(j\) 的点的权值之和。

image

有:

\[\begin{cases} dp[u][k] = \sum dp[to][k - 1] \f[to][j] = dp[to][j] - dp[to][j - 2] + f[u][j - 1] \end{cases} \]


\(f[to][j] = dp[to][j] - dp[to][j - 2] + f[u][j - 1]\) 表示:

image

代码:

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 100010, M = 30;
  4. int w[N];
  5. struct Edge {
  6. int to;
  7. int next;
  8. }e[N * 2];
  9. int head[N], idx;
  10. void add(int a, int b) {
  11. idx++;
  12. e[idx].to = b;
  13. e[idx].next = head[a];
  14. head[a] = idx;
  15. }
  16. int n, k;
  17. int dp[N][M];
  18. int f[N][M];
  19. void dfs(int u, int fa) {
  20. for (int i = 0; i <= k; i++) dp[u][i] = w[u];
  21. for (int i = head[u]; i; i = e[i].next) {
  22. int to = e[i].to;
  23. if (to == fa) continue;
  24. dfs(to, u);
  25. for (int i = 1; i <= k; i++) dp[u][i] += dp[to][i - 1];
  26. }
  27. }
  28. void dfs2(int u, int fa) {
  29. for (int i = head[u]; i; i = e[i].next) {
  30. int to = e[i].to;
  31. if (to == fa) continue;
  32. f[to][1] = dp[to][1] + w[u];
  33. for (int j = k; j >= 2; j--) f[to][j] = f[u][j - 1] - dp[to][j - 2] + dp[to][j];
  34. dfs2(to, u);
  35. }
  36. }
  37. int main() {
  38. ios::sync_with_stdio(false);
  39. cin.tie(nullptr);
  40. cin >> n >> k;
  41. for (int i = 1; i < n; i++) {
  42. int a, b;
  43. cin >> a >> b;
  44. add(a, b);
  45. add(b, a);
  46. }
  47. for (int i = 1; i <= n; i++) cin >> w[i];
  48. dfs(1, 0);
  49. memcpy(f[1], dp[1], sizeof(f[1]));
  50. dfs2(1, 0);
  51. for (int i = 1; i <= n; i++) cout << f[i][k] << '\n';
  52. return 0;
  53. }

原文链接:https://www.cnblogs.com/PlayWithCPP/p/17586872.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号