题解 P4408 【[NOI2003]逃学的小孩】

huyufeifei

2018-11-25 11:20:27

Solution

大部分思路好像都是先找直径然后再找起点。 我提供另一种思路:找中间点。 可以发现一种合法的路径其实就是在一个点处拉出去三条链(长度可以为0),其中第二长的链长度翻倍。 然后就用树形DP+二次扫描与换根法做了。 有一个细节就是这三条链不能重复,所以只取每个子树的最长链拿来更新。 代码: ```cpp #include <cstdio> #include <algorithm> #include <cstring> typedef long long LL; const int N = 200010; struct Edge { int nex, v, len; }edge[N << 1]; int top; int n, e[N]; LL f[N][3], t[7], ans; inline void add(int x, int y, int z) { top++; edge[top].v = y; edge[top].len = z; edge[top].nex = e[x]; e[x] = top; return; } void DFS(int x, int fa) { for(int i = e[x]; i; i = edge[i].nex) { int y = edge[i].v; if(y == fa) { continue; } DFS(y, x); for(int j = 0; j < 3; j++) { if(f[x][j] < f[y][0] + edge[i].len) { for(int k = 2; k > j; k--) { f[x][k] = f[x][k - 1]; } f[x][j] = f[y][0] + edge[i].len; break; } } } for(int i = 0; i < 3; i++) { if(f[x][i] < 0) { f[x][i] = 0; break; } } return; } void DFS_2(int x, int fa, LL dis) { t[3] = dis; for(int i = 0; i < 3; i++) { t[i] = f[x][i]; } std::sort(t, t + 4); ans = std::max(ans, t[3] + t[2] + t[2] + t[1]); LL a = t[3], b = t[2]; for(int i = e[x]; i; i = edge[i].nex) { int y = edge[i].v; if(y == fa) { continue; } if(f[y][0] + edge[i].len == a) { DFS_2(y, x, b + edge[i].len); } else { DFS_2(y, x, a + edge[i].len); } } return; } int main() { memset(f, ~0x3f, sizeof(f)); int m; scanf("%d%d", &n, &m); for(int i = 1, x, y, z; i <= m; i++) { scanf("%d%d%d", &x, &y, &z); add(x, y, z); add(y, x, z); } DFS(1, 0); DFS_2(1, 0, 0ll); printf("%lld", ans); return 0; } ```