我有一个思路是这样的:考虑dp[u][0/1]表示当前点是否作为祖先关系的两段蓝边的中点。蓝边只有两种形态,一种是祖先关系的直链,一种是折过来。因为一个点如果作为直链蓝边的中点,那就只能过这一次;不作为中点的话可以随便挂直链,也可以挂一个折链,但是折链只能挂一个。
于是我考虑转移:首先不考虑折链的话,dp[u][0]=∑v∈son[u]max(dp[v][0],dp[v][1]+e[i].w)。 考虑直链就相当于选一个儿子强行作为蓝线的一头,也就是选一个儿子从max(dp[v][0],dp[v][1]+e[i].w)换成dp[v][0]+e[i].w,其余的儿子不变;考虑折链就相当于是选两个儿子这样替换,所以找一个d[v][0]+e[i].w−max(dp[v][0],dp[v][1]+e[i].w)的最大值和次大值替换下来就行了。
然后就WA得只有20分QwQ有大佬可以指出一下这样dp哪里错了吗。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 200005
using namespace std;
typedef long long ll;
int read() {
int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
struct edge {int to, w, nxt;} e[maxn << 1];
int head[maxn], k = 0;
void add(int u, int v, int w) {e[k] = {v, w, head[u]}; head[u] = k++;}
int n, f[maxn][2], fa[maxn];
void dfs(int u) {
bool flag = false; int tmp, mx = -(0x3f3f3f3f), mx2 = -(0x3f3f3f3f);
f[u][1] = -(0x3f3f3f3f), f[u][0] = 0;
for(int i = head[u]; ~i; i = e[i].nxt) {
register int v = e[i].to; if(v == fa[u]) continue;
flag = true;
fa[v] = u; dfs(v);
tmp = max(f[v][0], f[v][1] + e[i].w);
f[u][0] += tmp;
f[u][1] = max(f[u][1], f[v][0] + e[i].w - tmp);
tmp = f[v][0] + e[i].w - tmp;
if(tmp >= mx) mx2 = mx, mx = tmp;
else if(tmp >= mx2) mx2 = tmp;
}
f[u][1] += f[u][0]; if(mx + mx2 > 0) f[u][0] += mx + mx2;
}
signed main() {
memset(head, -1, sizeof head);
n = read();
for(int i = 1, u, v, w; i < n; i++) u = read(), v = read(), w = read(), add(u, v, w), add(v, u, w);
dfs(1);
printf("%d\n", f[1][0]);
return 0;
}