BZOJ1912 最长链树形DP
每次求出最长链更新答案后要将最长链上的边权改为-1
写的贼长 还可以优化...
1 /*Huyyt*/ 2 #include<bits/stdc++.h> 3 #define mem(a,b) memset(a,b,sizeof(a)) 4 #define pb push_back 5 using namespace std; 6 typedef long long ll; 7 typedef unsigned long long ull; 8 using namespace std; 9 const int MAXN = 1e5 + 5, MAXM = 2e5 + 5; 10 int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], ed = 1; 11 int value[MAXM << 1]; 12 inline void addedge(int u, int v, int val) 13 { 14 to[++ed] = v; 15 nxt[ed] = Head[u]; 16 value[ed] = val; 17 Head[u] = ed; 18 } 19 int d[MAXN]; 20 void dfs(int x, int pre) 21 { 22 for (int v, i = Head[x]; i; i = nxt[i]) 23 { 24 v = to[i]; 25 if (v == pre) 26 { 27 continue; 28 } 29 d[v] = d[x] + value[i]; 30 dfs(v, x); 31 } 32 } 33 void change(int x) 34 { 35 for (int v, i = Head[x]; i; i = nxt[i]) 36 { 37 v = to[i]; 38 if (d[v] == d[x] - 1) 39 { 40 value[i] = value[i ^ 1] = -1; 41 change(v); 42 } 43 } 44 } 45 int s, t, dmx = -1; 46 int ans2 = 0, vis[MAXN], dpd[MAXN]; 47 void dp(int x) 48 { 49 vis[x] = 1; 50 for (int v, i = Head[x]; i; i = nxt[i]) 51 { 52 v = to[i]; 53 if (vis[v]) 54 { 55 continue; 56 } 57 dp(v); 58 ans2 = max(ans2, dpd[x] + dpd[v] + value[i]); 59 dpd[x] = max(dpd[x], dpd[v] + value[i]); 60 } 61 } 62 int main() 63 { 64 int anser; 65 int n, k; 66 int u, v; 67 scanf("%d %d", &n, &k); 68 for (int i = 1; i < n; i++) 69 { 70 scanf("%d %d", &u, &v); 71 addedge(u, v, 1), addedge(v, u, 1); 72 } 73 anser = 2 * (n - 1); 74 d[1] = 0; 75 dfs(1, 0); 76 for (int i = 1; i <= n; i++) 77 { 78 if (d[i] > dmx) 79 { 80 dmx = d[i]; 81 s = i; 82 } 83 } 84 d[s] = 0; 85 dfs(s, 0); 86 dmx = -1; 87 for (int i = 1; i <= n; i++) 88 { 89 if (d[i] > dmx) 90 { 91 dmx = d[i]; 92 t = i; 93 } 94 } 95 anser -= d[t] - 1; 96 if (k == 1) 97 { 98 printf("%d\n", anser); 99 return 0; 100 } 101 change(t); 102 dp(1); 103 anser -= ans2 - 1; 104 printf("%d\n", anser); 105 return 0; 106 }
求树直径dp
1 void dp(int x) 2 { 3 vis[x] = 1; 4 for (int v, i = Head[x]; i; i = nxt[i]) 5 { 6 v = to[i]; 7 if (vis[v]) 8 { 9 continue; 10 } 11 dp(v); 12 ans2 = max(ans2, dpd[x] + dpd[v] + value[i]); 13 dpd[x] = max(dpd[x], dpd[v] + value[i]); 14 } 15 }
其实这个dp的作用是先把无根树转化为有根树 再求每个点子树中的最长链和次长链(如果有次长链的话)
则树的直径有两种情况
1.是一个节点的最长链
2.是一个节点的次长链+最长链
我们首先记录直径取最长是在哪个节点 然后在每个节点我们都要记录 次长链是那条边拓展出去和最长链是那条边拓展出去
因为一个节点的最长链和次长链必定是一个边加下一个节点的最长链
这样就可以一个dfs搞定
1 /*Huyyt*/ 2 #include<bits/stdc++.h> 3 #define mem(a,b) memset(a,b,sizeof(a)) 4 #define pb push_back 5 using namespace std; 6 typedef long long ll; 7 typedef unsigned long long ull; 8 using namespace std; 9 const int MAXN = 1e5 + 5, MAXM = 1e5 + 5; 10 int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], ed = 1; 11 int value[MAXM << 1]; 12 inline void addedge(int u, int v, int val) 13 { 14 to[++ed] = v; 15 nxt[ed] = Head[u]; 16 value[ed] = val; 17 Head[u] = ed; 18 } 19 int mxlen[MAXN], mxlen2[MAXN]; 20 int ansdis = 0; //直径大小 21 int s, t; 22 int dfs(int x, int pre) 23 { 24 int mx1 = 0, mx2 = 0; //当前节点的最长链和次长链长度 25 int now; 26 for (int v, i = Head[x]; i; i = nxt[i]) 27 { 28 v = to[i]; 29 if (v == pre) 30 { 31 continue; 32 } 33 now = dfs(v, x) + value[i]; 34 if (now > mx1) 35 { 36 mx2 = mx1; 37 mxlen2[x] = mxlen[x]; 38 mx1 = now; 39 mxlen[x] = i; //更新最长链 原最长链变为次长链 40 } 41 else if (now > mx2) 42 { 43 mx2 = now; 44 mxlen2[x] = i; //更新次长链 45 } 46 } 47 if (mx1 + mx2 > ansdis) 48 { 49 ansdis = mx1 + mx2; 50 s = x; 51 } 52 return mx1;//返回每个节点的最长链大小 53 } 54 int main() 55 { 56 int anser; 57 int n, k; 58 int u, v; 59 scanf("%d %d", &n, &k); 60 for (int i = 1; i < n; i++) 61 { 62 scanf("%d %d", &u, &v); 63 addedge(u, v, 1), addedge(v, u, 1); 64 } 65 anser = 2 * (n - 1); 66 dfs(1, 0); 67 anser -= ansdis - 1; 68 if (k == 1) 69 { 70 printf("%d\n", anser); 71 return 0; 72 } 73 ansdis = 0; 74 for (int i = mxlen[s]; i; i = mxlen[to[i]]) //最长链上的边重置为-1 75 { 76 value[i] = value[i ^ 1] = -1; 77 } 78 for (int i = mxlen2[s]; i; i = mxlen[to[i]]) //次长链上的边重置为-1 79 { 80 value[i] = value[i ^ 1] = -1; 81 } 82 dfs(1, 0); 83 anser -= ansdis - 1; 84 printf("%d\n", anser); 85 return 0; 86 }