【jzoj3861】【JSOI2014】【支线剧情2 】【树形动态规划】

题目大意

给出一棵有根树,走过每一条边有一定的费用,只能从根方向往叶子方向走。你只有一个存档位,你可以选择途中的一个地方存档,任何时候你可以读档,回到存档位,也可以回到根。求最少的费用走过所有点。

解题思路

设f[i]表示i及其子树无存档从i走完子树的花费。g[i]表示i有存档走完子树的花费。f可以很暴力地求,所有都从根开始走。g我们先找一个子节点走f比走g最劣,先将它走g。在扫其他节点走f和g那个优就走那个,注意这时g要加上从根走到子节点的花费。

code

#include
#include
#include
#include
#include
#define LL long long
#define min(a,b) ((ab)?a:b)
#define fo(i,j,k) for(LL i=j;i<=k;i++)
#define fd(i,j,k) for(LL i=j;i>=k;i--)
using namespace std;
LL const maxn=1e6,mo=12580;
LL n,gra,begin[maxn+10],to[maxn+10],len[maxn+10],next[maxn+10],
dep[maxn+10],f[maxn+10],g[maxn+10],son[maxn+10];
void insert(LL u,LL v,LL w){
    to[++gra]=v;
    len[gra]=w;
    next[gra]=begin[u];
    begin[u]=gra;
}
void dfs(LL now){
    if(!begin[now]){son[now]=1;return;}
    LL p=begin[now],tmp=0;
    for(LL i=begin[now];i;i=next[i]){
        dep[to[i]]=dep[now]+len[i];
        dfs(to[i]);
        son[now]+=son[to[i]];
        f[now]+=f[to[i]]+len[i]*son[to[i]];
        if(f[to[i]]+len[i]*son[to[i]]-g[to[i]]-len[i]>f[to[p]]+len[p]*son[to[p]]-g[to[p]]-len[p])p=i;
    }
    for(LL i=begin[now];i;i=next[i])if(i!=p){
        if(f[to[i]]+len[i]*son[to[i]]>=g[to[i]]+dep[to[i]])g[now]+=g[to[i]]+dep[to[i]];
        else g[now]+=f[to[i]]+len[i]*son[to[i]];
    }
    g[now]+=g[to[p]]+len[p];
}
LL read(){
    LL v=0;char ch=getchar();
    for(;(ch<'0')||(ch>'9');ch=getchar());
    for(;(ch>='0')&&(ch<='9');v=v*10+ch-'0',ch=getchar());
    return v;
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    n=read();LL K,b,t;
    fo(i,1,n){
        K=read();
        fo(j,1,K){
            b=read();t=read();
            insert(i,b,t);
        }
    }
    dfs(1);
    printf("%lld",g[1]);
    return 0;
}

你可能感兴趣的:(动态规划,jzoj)