http://uoj.ac/problem/105
好神的dp啊。
确定一个点为根之后,蓝线只能是竖着的,不能横跨兄弟。
枚举每个点为根进行树形dp是(O(n^2))的,(f(x,0/1))表示以(x)为根的子树中(x)是否作为蓝线终点的最大值。
更科学的做法:(O(1))把根从一个father转移到它的son。
需要维护(f(father,1))的最大和次大(防止son作为最大转移到father),利用father的信息更新(f(son,0))和(f(son,1))的最大和次大(这里的换根不是真正把根换到son,只是说换根后son作为根的信息是正确的,不需要修改father的信息)。
时间复杂度(O(n))。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 200003;
struct node {int nxt, to, w;} E[N << 1];
int cnt = 0, point[N], f[N][2], fs[N][2];
void ins(int u, int v, int w) {E[++cnt] = (node) {point[u], v, w}; point[u] = cnt;}
int n, son[N], tot, fadis[N];
void dfs(int x, int fa) {
for (int i = point[x]; i; i = E[i].nxt)
if (E[i].to != fa) fadis[E[i].to] = E[i].w, dfs(E[i].to, x);
tot = 0;
for (int i = point[x]; i; i = E[i].nxt)
if (E[i].to != fa) son[++tot] = E[i].to;
for (int i = 1; i <= tot; ++i)
f[x][0] += max(f[son[i]][0], f[son[i]][1]);
int mx = -0x7fffffff, mxs = -0x7fffffff, num;
for (int i = 1; i <= tot; ++i) {
num = f[son[i]][0] + fadis[son[i]] - max(f[son[i]][0], f[son[i]][1]);
if (num >= mx) mxs = mx, mx = num;
else if (num > mxs) mxs = num;
}
f[x][1] = mx + fadis[x] + f[x][0];
fs[x][1] = mxs + fadis[x] + f[x][0];
}
void move(int x, int y, int d) {
int t, fx0 = f[x][0], fx1 = f[x][1], fxs1 = fs[x][1];
if (fx1 == fx0 - max(f[y][1], f[y][0]) + f[y][0] + fadis[y]) fx1 = fxs1;
fx0 -= max(f[y][1], f[y][0]);
fx1 -= max(f[y][1], f[y][0]);
fx1 += d;
f[y][0] += max(fx0, fx1);
f[y][1] += max(fx0, fx1);
fs[y][1] += max(fx0, fx1);
f[y][1] -= d; fs[y][1] -= d;
if ((t = f[y][0] - max(fx0, fx1) + fx0 + d) >= f[y][1])
fs[y][1] = f[y][1], f[y][1] = t;
else if (t > fs[y][1]) fs[y][1] = t;
}
int ans = 0;
void dfsmove(int x, int fa) {
ans = max(ans, f[x][0]);
for (int i = point[x]; i; i = E[i].nxt) {
int v = E[i].to;
if (v == fa) continue;
move(x, v, E[i].w);
dfsmove(v, x);
}
}
int main() {
scanf("%d", &n);
int u, v, e;
for (int i = 1; i < n; ++i) {
scanf("%d%d%d", &u, &v, &e);
ins(u, v, e);
ins(v, u, e);
}
dfs(1, 0);
dfsmove(1, 0);
printf("%d
", ans);
return 0;
}