【JSOI2014】支线剧情2

Description

【JSOI2014】支线剧情2_第1张图片

Solution

这题明显是DP。
设f[i][0]和f[i][1]表示以i为根的子树打不打标记的最小值。
设shu[x]表示x为根的子树有多少个叶子节点。
f[x][0]+=f[last[i]]+chang[i]*shu[last[i]]
f[x][0]的转移很显然,因为子节点与x要走叶节点的个数次。
f[x][1]的转移就有好几种情况:
1:儿子打了标记,那么走完儿子的f[last[i]][1]之后,还要重新从根节点走下来
2:儿子没打标记,走完儿子的f[last[i]][0]之后,还要重新从x开始
3:还可以选一个儿子打标记,其他儿子不打标记,就先走不打标记的儿子,然后走打标记的儿子。
然后就没有了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
typedef long long ll;
const int maxn=1e6+7;
int i,j,k,l,t,n,m,son[maxn];
int first[maxn],next[maxn],last[maxn],num,chang[maxn],deep[maxn];
ll f[maxn][2],ans;
int fa[maxn],shu[maxn],shun[maxn];
void add(int x,int y,int z){
    last[++num]=y,next[num]=first[x],first[x]=num;chang[num]=z;
}
void dfs(int x,int y){
    if(!first[x])shu[x]=1;
    ll i,p=0,q=0;
    rep(i,x){
        dfs(last[i],y+chang[i]);
        shu[x]+=shu[last[i]];
        f[x][0]+=f[last[i]][0]+shu[last[i]]*chang[i];
        p=min(min(p+f[last[i]][1]+y+chang[i],p+f[last[i]][0]+chang[i]*shu[last[i]]),q+chang[i]+f[last[i]][1]);
        q=f[x][0];
    }
    f[x][1]=min(p,f[x][0]);
}
int main(){
//  freopen("fan.in","r",stdin);
    scanf("%d",&n);
    fo(i,1,n){
        scanf("%d",&son[i]);
        fo(j,1,son[i]){
            scanf("%d%d",&k,&l);add(i,k,l);
        }
    }
    dfs(1,0);
    ans=min(f[1][0],f[1][1]);
    printf("%lld\n",ans);
}

你可能感兴趣的:(DP,省选)