P3647连珠线
观察题目,我们可以发现一个性质:蓝色的边都是由三个点成对出现的。
假设当前节点为 $i$,当前节点的父亲为 $fa$,当前节点的儿子为 $j$。在根不确定时,一对蓝边可以有两种组成方式:
- 由 $fa$,$i$,$j$ 连边。
- 由 $j_1$,$i$,$j_2$ 连边,即儿子通过当前节点相互连边。
如果我们在根不确定的情况下进行 DP 的话,没有办法判断不合法的情况。因此我们就要枚举每一个节点作为根的情况。而当树的形态确定时只能存在第 $1$ 种连边。设 $f_{i,0/1}$ 表示一个点是否是一对蓝边的中点( $1$ 表示当前节点是中点),然后我们可以写出当根确定时的转移方程:
$update(i, j, w) :$
$$sum_i = \sum \max \left \{ f_{j,0} ,f_{j,1} +w\right \}$$
$$f_{i, 0} = sum_i $$
$$f_{i,1} = sum_i + \max \left \{f_{j, 0} + w -\max \left \{ f_{j,0}, f_{j,1} + w\right \} \right \}$$
因为这是 $O(n)$ 的,然后我们就需要想如何 $O(1)$ 进行换根。
我们要思考换根会影响那部分的贡献。在这一题中当节点 $i$ 换到它的儿子 $j$ 时只会影响他们之间的贡献。具体来说,$sum_i$ 要减掉 $j$ 的贡献,更新 $f_{i,1}$ 时最大值可能就是来自 $j$,所以需要再维护最大/次大值,然后再去按照第一次 DP 的方法更新 $j$。 设 $max1$,$max2$ 分别表示最大/次大值。
$root_i \to root_j :$
$$sum_i \gets sum_i - \max \left \{ f_{j, 0}, f_{j ,1} + w \right \}$$
$$f_{i, 0} = sum_i $$
$if (max1.id = j) $
$$f_{j, 1} = sum_i + max2.v$$$else$
$$f_{j, 1} = sum_i + max1.v$$$$update(j, i, w)$$
因为我们是从父亲向儿子转移的,在更新完一个儿子后父亲的状态记得还原回去,然后再去更新其他儿子。
Code
#include<bits/stdc++.h>
using namespace std;
#define rp(i, a, b) for(int i = a;i <= b;i++)
#define FR for(int i = head[u], v = e[i].to;i;i = e[i].next, v = e[i].to)
const int N = 2e5 + 5;
struct edge { int next, to, w; } e[N << 1];
struct node { int v, id; } Maxo[N], Maxt[N];
int cnt, n, ans = 0;
int head[N], f[N][2], Sum[N];
void add(int f, int t, int w) {
e[++cnt] = edge {head[f], t, w};
head[f] = cnt;
}
void update(int u, int v, int w) {
int d = max(f[v][0], f[v][1] + w);
int dd = -d + f[v][0] + w;
Sum[u] += d;
if(dd > Maxo[u].v) Maxt[u] = Maxo[u], Maxo[u] = node {dd, v};
else if(dd > Maxt[u].v) Maxt[u] = node {dd, v};
}
void dfs(int u, int fa) {
Maxo[u].v = -1e8, Maxt[u].v = -1e8, Sum[u] = 0;
FR {
if(v == fa) continue;
dfs(v, u);
update(u, v, e[i].w);
}
f[u][1] = Sum[u] + Maxo[u].v;
f[u][0] = Sum[u];
ans = max(ans, f[u][0]);
}
void solve(int u, int fa) {
int sum = Sum[u], f1 = f[u][0], f2 = f[u][1];
FR {
if(v == fa) continue;
Sum[u] -= max(f[v][0], f[v][1] + e[i].w);
f[u][0] = Sum[u];
f[u][1] = Sum[u] + (Maxo[u].id == v ? Maxt[u].v : Maxo[u].v);
update(v, u, e[i].w);
Sum[u] = sum, f[u][0] = f1, f[u][1] = f2;
f[v][1] = Sum[v] + Maxo[v].v;
f[v][0] = Sum[v];
ans = max(ans, f[v][0]);
solve(v, u);
}
}
int main(){
scanf("%d", &n);
rp(i ,1, n - 1) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
dfs(1, 0);
solve(1, 0);
printf("%d", ans);
return 0;
}

浙公网安备 33010602011771号