【JSOI2014】支线剧情2

来源:互联网 发布:8051单片机引脚映射 编辑:程序博客网 时间:2024/04/29 16:53

Description

这里写图片描述

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<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>#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);}
1 0