P3647连珠线

观察题目,我们可以发现一个性质:蓝色的边都是由三个点成对出现的。
假设当前节点为 $i$,当前节点的父亲为 $fa$,当前节点的儿子为 $j$。在根不确定时,一对蓝边可以有两种组成方式:

  1. 由 $fa$,$i$,$j$ 连边。
  2. 由 $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;
}
posted @ 2023-09-16 08:46  Saka_Noa  阅读(21)  评论(0)    收藏  举报  来源