牛客小白70-F

发布时间 2023-04-08 20:01:41作者: 安潇末痕

题目链接:https://ac.nowcoder.com/acm/contest/53366/F

经典树形背包dp,应该是会写的,但比赛的时候怎么也写不出,还是不够熟练。

转移方程与板子差不多,只有枚举给子树的k值大于等于他的子节点个数时,这条边就没有加的必要了。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int dp[N][55],temp[55];
int h[N],ne[2*N],num[2*N],idx;
int sz[N];
bool st[N];
int n,m;
int ans = -2;
void add(int x,int y){
    num[idx] = y,ne[idx] = h[x],h[x] = idx++;
}
void dfs1(int u,int fa){
    if (st[u]) sz[u] = 1;
    for (int i=h[u];i!=-1;i=ne[i]){
        int v = num[i];
        if (v==fa) continue;
        dfs1(v,u);
        sz[u] += sz[v];
    }
}
void dfs(int u,int fa){
    ans += 2*(sz[u]>0);
    for (int i=h[u];i!=-1;i=ne[i]){
        int v = num[i];
        if (v==fa) continue;
        dfs(v,u);
        memset(temp,0,sizeof(temp));
        for (int j=0;j<=min(m,sz[u]);j++){
            for (int k=0;k<=min(m,sz[v]);k++){
                if (j+k<=m) temp[j+k] = max(temp[j+k],dp[u][j]+dp[v][k]+2*(k&&k==sz[v]));
            }
        }
        for (int j=0;j<=min(m,sz[u]);j++) dp[u][j] = temp[j]; 
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    memset(h,-1,sizeof(h));
    cin>>n>>m;
    for (int i=1;i<n;i++){
        int x,y;
        cin>>x>>y;
        add(x,y),add(y,x);
    }
    int k;
    cin>>k;
    for (int i=1;i<=k;i++){
        int x;
        cin>>x;
        st[x] = 1;
    }
    dfs1(1,-1);
    dfs(1,-1);
    //cout<<dp[2][1]<<'\n';
    cout<<ans-dp[1][m]<<'\n';
}