HDU5886 Tower Defence 【两遍树形dp】【最长链预处理】
题意:N个点的一棵带权树。切掉某条边的价值为切后两树直径中的最大值。求各个边切掉后的价值和(共N-1项)。
解法一:
强行两遍dp,思路繁琐,维护东西较多:
dis表示以i为根的子树的直径,dis2表示切掉以i为根的子树后的直径。
第一遍dp,记录
down[][0]:从i结点向下的最大距离
down[][1]:与down[][0]无交集的向下次大距离
dis:以i为根的子树的直径
第二遍dp,记录
up:从i结点向上的最远距离, 可以是w+父节点的up,也可以是w+父节点的down(判断一下down是否与w有重合)
dis2:切掉以i为根的子树后的直径
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define X first 4 #define Y second 5 #define pii pair<int, int> 6 #define mp make_pair 7 typedef long long ll; 8 const int N = 1e5+5; 9 void gmax(int& a, int b){ if(a < b) a = b;} 10 int n; 11 ll ans; 12 int down[N][2], up[N], dis[N], dis2[N]; 13 14 int head[N], nex[N*2], tot; 15 pii edge[N*2]; 16 void init(){ 17 tot = 0; 18 memset(head, -1, sizeof(head)); 19 memset(down, 0, sizeof(down)); 20 memset(up, 0, sizeof(up)); 21 memset(dis, 0, sizeof(dis)); 22 memset(dis2, 0, sizeof(dis2)); 23 } 24 void add(int u, int v, int w){ 25 edge[tot] = mp(v, w); 26 nex[tot] = head[u]; 27 head[u] = tot++; 28 } 29 //down[][0]:从i结点向下的最大距离 30 //down[][1]:与down[][0]无交集的向下次大距离 31 //dis:以i为根的子树的直径 32 void dfs(int fa, int x){ 33 // printf("dfs x %d, fa %d\n", x, fa); 34 //down[x][0] = down[x][1] = dis[x] = dis2[x] = up[x] = 0; 35 for(int i = head[x]; ~i; i = nex[i]){ 36 int y = edge[i].X, w = edge[i].Y; 37 if(y == fa) continue ; 38 dfs(x, y); 39 if(down[y][0]+w > down[x][0]) 40 down[x][1] = down[x][0], down[x][0] = down[y][0]+w; 41 else if(down[y][0]+w > down[x][1]) 42 down[x][1] = down[y][0]+w; 43 gmax(dis[x], dis[y]); 44 } 45 gmax(dis[x], down[x][0]+down[x][1]); 46 } 47 //up:从i结点向上的最远距离 48 //dis2:切掉以i为根的子树后的直径 49 multiset<int>::iterator it; 50 void dfs2(int fa, int x){ 51 if(fa != -1) ans += max(dis[x], dis2[x]); 52 // printf("fa %d, x %d\n", fa, x); 53 multiset<int> se, se2;//兄弟树的直径, x往各个兄弟树的最长路 54 for(int i = head[x]; ~i; i = nex[i]){ 55 int y = edge[i].X, w = edge[i].Y; 56 if(y == fa) continue ; 57 se.insert( dis[y] ); 58 se2.insert( down[y][0]+w ); 59 } 60 for(int i = head[x]; ~i; i = nex[i]){ 61 int y = edge[i].X, w = edge[i].Y; 62 if(y == fa) continue ; 63 up[y] = up[x]+w; 64 if(down[y][0]+w == down[x][0]) 65 gmax(up[y], down[x][1]+w); 66 else gmax(up[y], down[x][0]+w); 67 it = se.find( dis[y] ), se.erase(it); 68 it = se2.find( down[y][0]+w ), se2.erase(it); 69 dis2[y] = dis2[x]; 70 if(!se.empty()) 71 gmax(dis2[y], *se.rbegin()); 72 if(se2.empty()) gmax(dis2[y], up[x]); 73 else gmax(dis2[y], up[x]+*se2.rbegin()); 74 if(se2.size() >= 2){ 75 int tmp = 0; 76 it = se2.end(); 77 tmp += *--it; 78 tmp += *--it; 79 gmax(dis2[y], tmp); 80 } 81 dfs2(x, y); 82 se.insert( dis[y] ); 83 se2.insert( down[y][0]+w ); 84 } 85 } 86 void debug(int n){ 87 for(int i = 1; i <= n; i++) 88 printf("%d: down0 %d, down1 %d, dis %d, dis2 %d, up %d\n", i, down[i][0], down[i][1], dis[i], dis2[i], up[i]); 89 } 90 int main(){ 91 int t, u, v, w; scanf("%d", &t); 92 while(t--) { 93 init(); 94 scanf("%d", &n); 95 for(int i = 1; i < n; i++){ 96 scanf("%d%d%d", &u, &v, &w); 97 add(u, v, w), add(v, u, w); 98 } 99 ans = 0; 100 dfs(-1, 1); 101 dfs2(-1, 1); 102 printf("%lld\n", ans); 103 } 104 return 0; 105 }
解法二:
先求出原树的直径。
从直径两端分别来一次dp
切的边不在直径上,价值为直径;
切的边在直径上,由直径两端的dp得解。
诸神对凡人心生艳羡,厌倦天堂。