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

题目大意

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

解题思路

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

code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
#define min(a,b) ((a<b)?a:b)
#define max(a,b) ((a>b)?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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值