P3354 [IOI2005]Riv 河流

树形dp,设f[i][j][k]表示第i个点的子树中选择j个点作为伐木场,而且k是建了伐木场的最浅的i的祖先的情况下,最小的收益。

这种题还要练一下,咕咕

然后转移可以n4方做。

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il int gi(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,K,W[110],fa[110];
int fir[110],dis[110],nxt[110],id;
int w[110][110];
il vd link(int a,int b){nxt[++id]=fir[a],fir[a]=id,dis[id]=b;}
ll f[110][51][110],g[110][51][110];
int dep[110],stk[110],tp;
il vd dp(int x){
    stk[++tp]=x;
    for(int i=fir[x];i;i=nxt[i])dep[dis[i]]=dep[x]+w[dis[i]][1],dp(dis[i]);
    for(int tt=fir[x];tt;tt=nxt[tt]){
        int t=dis[tt];
        for(int k=1;k<=tp;++k)
            for(int i=K;~i;--i){//一共选i个
                f[x][i][stk[k]]+=f[t][0][stk[k]];
                g[x][i][stk[k]]+=f[t][0][x];
                for(int j=1;j<=i;++j){//t子树选j个
                    f[x][i][stk[k]]=std::min(f[x][i][stk[k]],f[x][i-j][stk[k]]+f[t][j][stk[k]]);
                    g[x][i][stk[k]]=std::min(g[x][i][stk[k]],g[x][i-j][stk[k]]+f[t][j][x]);
                }
            }
    }
    for(int i=1;i<=tp;++i)
        for(int j=0;j<=K;++j){
            f[x][j][stk[i]]+=W[x]*(dep[x]-dep[stk[i]]);
            if(j)f[x][j][stk[i]]=std::min(f[x][j][stk[i]],g[x][j-1][stk[i]]);
        }
    --tp;
}
int main(){
    n=gi(),K=gi();
    for(int i=1;i<=n;++i)W[i]=gi(),fa[i]=gi(),w[i][1]=gi(),link(fa[i],i);
    for(int i=1;i<=n;++i)
        for(int j=2;j<=n;++j)
            w[i][j]=w[fa[i]][j-1]+w[i][1];
    dp(0);
    printf("%lld
",f[0][K][0]);
    return 0;
}
原文地址:https://www.cnblogs.com/xzz_233/p/10040325.html