P8564 ρars/ey 題解

Night_Tide發表於2024-09-26

顯然樹上揹包。

首先一眼會想到的狀態:\(dp_{i,j}\) 表示 \(i\) 的子樹最後剩下 \(j\) 個結點的最小代價。

然而開始寫會發現這並不好 DP。

於是我們換一個想法:\(dp_{i,j}\) 表示 \(i\) 的子樹刪去 \(j\) 個結點的最小代價。

則有轉移方程:

\[dp_{i,j} = \min_{v \in son(i)}\{dp_{i, j - k} + dp_{v,k}\} \]

但是注意到這個方程 \(j\) 最多到 \(siz_{i} - 1 - cntson_{i}\)\(cntson_{i}\) 表示 \(i\) 的兒子的數量),我們還需要一個方程:

\[dp_{i,siz_{i} - 1} = \min\{dp_{i,j} + f_{siz_{i} - j}\} \]

於是這樣就完美了,\(O(n^2)\) 足夠透過此題

程式碼如下:

#include<bits/stdc++.h>
#define MAXN 5010
#define INF 0x7f7f7f7f7f7f7f7f
using namespace std;
typedef long long ll;
struct edge{ int pre, to; };
edge e[MAXN << 1];
int n, cnt;
int head[MAXN], f[MAXN];
namespace strange{
    ll dp[MAXN][MAXN], siz[MAXN];
    void dfs(int now, int fa){
        dp[now][0] = 0; siz[now] = 1;
        for(int i = head[now]; i; i = e[i].pre){
            if(e[i].to == fa) continue;
            dfs(e[i].to, now);
            for(int j = siz[now] + siz[e[i].to] - 1; j > 0; j--){
                for(int k = max(j - siz[now], 1ll); k <= siz[e[i].to] - 1 && k <= j; k++){
                    if(dp[e[i].to][k] >= INF || dp[now][j - k] >= INF) continue;
                    dp[now][j] = min(dp[now][j], dp[e[i].to][k] + dp[now][j - k]);
                }
            }
            siz[now] += siz[e[i].to];//記得先 DP 後加 siz,否則複雜度是假的,這個蒟蒻就是因為這個少了 30 分
        }
        for(int j = 0; j < siz[now] - 1; j++){
            dp[now][siz[now] - 1] = min(dp[now][siz[now] - 1], dp[now][j] + f[siz[now] - j]);
        }
    }
    void main(){
        memset(dp, 0x7f, sizeof(dp));
        dfs(1, 0);
        printf("%lld\n",dp[1][siz[1] - 1]);
    }
}
void add_edge(int u, int v){
    e[++cnt].pre = head[u];
    e[cnt].to = v;
    head[u] = cnt;
}
int main(){
    // freopen("T2ex2.in", "r", stdin);
    scanf("%d",&n);
    for(int i = 1; i < n; i++) scanf("%d",&f[i + 1]);
    for(int i = 1; i < n; i++){
        int u, v; scanf("%d%d",&u,&v);
        add_edge(u, v); add_edge(v, u);
    }
    strange::main();
    return 0;
}