Loading

P3574 题解 | 贪心,树形 dp

传送门

题意

给出一颗树,根为 1 号节点,点有点权 \(a\),求从根出发一条遍历每条边恰好两次的路径,使得以下式子最小:

\(t_i\) 为第一次到达一个点时走过的路径条数,特别地 \(t_1 = 2 \times n - 2\)(最后回到 1)

\[\min _ {i = 1} ^ n \left ( t_i + a_i \right ) \]

思路

显然是树形 dp,记 \(f_u\)\(u\) 子树内的最小值答案,\(g_u\) 走整个子树一遍后回到 \(u\) 的距离。

\[\begin{align*} f_u &\leftarrow \max \left ( f_u, f_v + g_u + 1 \right ) \\ g_u &\leftarrow g_u + g_v + 2 \end{align*} \]

每遍历一个儿子都执行上述转移。
每次执行时,第一个式子里的 \(g_u\) 表示遍历 \(v\) 以前的所有儿子走一遍再回到 \(u\) 的代价,\(+1\) 则是到 \(v\) 的代价。

于是关键在于遍历儿子的顺序,考虑贪心。考虑通过调整法来推导出最优解的性质。

如,两个儿子 \(v_1, v_2\)。调整前遍历 \(v_1\) 后遍历 \(v_2\)

那么从 \(v_1,v_2\) 都没遍历向都遍历的转移为:

\[f_u \leftarrow \max \left( f_u, g_u + 1 + \max \left( f_{v_1} , g_{v_1} + 2 + f_{v_2}\right)\right) \]

若交换 \(v_1, v_2\)

\[f_u \leftarrow \max \left( f_u, g_u + 1 + \max \left( f_{v_2} , g_{v_2} + 2 + f_{v_1}\right)\right) \]

那么 \(v_1\)\(v_2\) 前面优于 \(v_2\)\(v_1\) 前面(不交换优于交换)的条件为:

\[\max \left( f_{v_1}, g_{v_1} + 2 + f_{v_2}\right) \lt \max \left( f_{v_2}, g_{v_2} + 2 + f_{v_1}\right) \]

注意到:\(g_{v_1} + 2 + f_{v_2} \gt f_{v_2}\),所以,若满足条件,右侧必须是第二个值成为 \(\max\),即:

\[\max \left( f_{v_1}, g_{v_1} + 2 + f_{v_2}\right) \lt g_{v_2} + 2 + f_{v_1} \]

\(f_{v_1} \lt g_{v_2} + 2 + f_{v_1}\) 恒成立,则有用的只有:

\[g_{v_1} + 2 + f_{v_2} \lt g_{v_2} + 2 + f_{v_1} \]

移项得:

\[g_{v_1} - f_{v_1} \lt g_{v_2} - f_{v_2} \]

于是每个 \(u\),将儿子按照 \(g_{v} - f_v\) 从小到大排序,然后转移即可。

代码:

link

const int N = 1e6 + 5;
int n;
int a[N];
vector<int> e[N];
int f[N], g[N];
void dfs(int u, int fa){
    if(u != 1) f[u] = a[u];
    vector<int> vec;
    for(int v : e[u]){
        if(v == fa) continue;
        dfs(v, u);
        vec.push_back(v);
    }
    sort(vec.begin(), vec.end(), [](int v1, int v2){
        return g[v1] - f[v1] < g[v2] - f[v2];
    });
    for(int v : vec){
        chkmx(f[u], g[u] + f[v] + 1);
        g[u] += g[v] + 2;
    }
}
void solve_test_case(){
    n = read();
    rep(i, 1, n){
        a[i] = read();
    }
    rep(i, 1, n - 1){
        int u = read(), v = read();
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(1, 0);
    write(max(f[1], g[1] + a[1]));
}
posted @ 2025-10-07 12:53  lajishift  阅读(7)  评论(0)    收藏  举报