洛谷P3354 河流
有点权边权的树,选出k个关键点,根必须选。每个点的贡献为点权 * 到最近的关键祖先的距离。求最小总贡献。
解:树形DP是最毒瘤的算法......
设fxij表示以x为根的子树中选了j个关键点,且x的最近关键祖先是它的i级祖先时的最小贡献。
初态:fxi0 = val[x] * dis(),fx01 = 0。
转移:注意到各个i之间是分层转移的,只有女儿i = 0的情况会转移到母亲的每个i。
于是先转移fy0r,再分层转移fyjr。每层是一个树上背包。
1 #include <bits/stdc++.h> 2 3 typedef long long LL; 4 const int N = 110; 5 6 struct Edge { 7 int nex, v, len; 8 }edge[N]; int tp; 9 10 int e[N], n, siz[N], fa[N], val[N], k; 11 LL f[N][N][N], temp[N][N], d[N]; 12 13 inline void add(int x, int y, int z) { 14 tp++; 15 edge[tp].v = y; 16 edge[tp].len = z; 17 edge[tp].nex = e[x]; 18 e[x] = tp; 19 return; 20 } 21 22 inline LL dis(int x, int t) { 23 int y = x; 24 for(int i = 1; i <= t; i++) { 25 y = fa[y]; 26 } 27 return d[x] - d[y]; 28 } 29 /* 30 2 1 31 1 0 2 32 1 0 3 33 ------------- 2 34 */ 35 void DFS(int x) { 36 //printf("x = %d d[x] = %lld fa[x] = %d \n", x, d[x], fa[x]); 37 siz[x] = 1; 38 for(int i = 1; i <= n; i++) { 39 f[x][i][0] = val[x] * dis(x, i); 40 //printf("f %d %d %d = %lld = %d * %d\n", x, i, 0, f[x][i][0], val[x], dis(x, i)); 41 } 42 f[x][0][1] = 0; 43 for(int i = e[x]; i; i = edge[i].nex) { 44 int y = edge[i].v; 45 //printf("%d -> %d \n", x, y); 46 d[y] = d[x] + edge[i].len; 47 DFS(y); 48 memcpy(temp, f[x], sizeof(f[x])); 49 memset(f[x], 0x3f, sizeof(f[x])); 50 for(int j = 0; j <= n; j++) { /// dis 51 for(int p = 1; p <= k; p++) { 52 for(int r = 1; r <= p; r++) { 53 //printf("r = %d -> %lld \n", r, temp[j][p - r] + f[y][0][r]); 54 f[x][j][p] = std::min(f[x][j][p], temp[j][p - r] + f[y][0][r]); /// don't choose 55 } 56 //printf("1f %d %d %d = %lld \n", x, j, p, f[x][j][p]); 57 } 58 } 59 for(int j = 0; j <= n; j++) { 60 for(int p = k; p >= 0; p--) { 61 for(int r = 0; r <= p; r++) { 62 f[x][j][p] = std::min(f[x][j][p], temp[j][p - r] + f[y][j + 1][r]); /// choose y 63 } 64 //printf("2f %d %d %d = %lld \n", x, j, p, f[x][j][p]); 65 } 66 } 67 siz[x] += siz[y]; 68 } 69 //printf("%d return \n", x); 70 return; 71 } 72 73 int main() { 74 memset(f, 0x3f, sizeof(f)); 75 scanf("%d%d", &n, &k); 76 n++; k++; 77 k = std::min(k, n); 78 for(int i = 2, x, y; i <= n; i++) { 79 scanf("%d%d%d", &val[i], &x, &y); 80 add(x + 1, i, y); 81 fa[i] = x + 1; 82 } 83 84 DFS(1); 85 86 printf("%lld\n", f[1][0][k]); 87 return 0; 88 }
注意每次合并子树的时候,原来的值不能保留。保留下来的要加上fy0r。
恶心死我了...