【jzoj3919】【COCI2014/2015 Round1 KAMP】【志愿者】【树形动态规划】

题目大意

FJ王国有N个村庄组成,村庄之间构成树型结构,村庄之间的公路是双向的。由于发洪水,现在FJ召集了K名奶牛志愿者,每名志愿者被派遣到一个村庄支援救助,不同的志愿者被派遣到不同的村庄。FJ要选择一个村庄作为救灾大本营,一开始所有的志愿者都在该村庄,FJ自己作为司机,要用汽车把这K名志愿者送到各自目的地。FJ出发前会把所有奶牛志愿者都装到他的汽车上,然后按照FJ自己设计的路线,最终把所有奶牛送到目的地。FJ最后不必返回大本营。由于村庄之间的公路的长度都不同,那么FJ应该如何设计行走路线才能使得汽车行驶总长度最短?为了更具体了解情况,FJ提问N个问题,第i个问题是:如果把大本营设置在第i个村庄,那么汽车行驶总长度最短是多少?其中1<=i<=N。你要依次回答这N个问题。

解题思路

考虑第i个询问,即i与K个点最小生成树减去i到K个点中最远点的距离。考虑K个点最小生成树加上两倍i到生成树上的距离减去i到K个点中最远点的距离。生成树可以选K个点中一个点dfs一次求解,有了生成树i到生成树上的距离也非常容易求出。i到K个点中最远点的距离由两部分组成,一个是子树中的部分mx,这个很容易求出,一个是剩余的部分g。v是u的父亲,g[u]=max(g[u],g[v]+dis(u,v))。mx不在u的子树,g[u]=max(g[u],mx[v]+dis(u,v))。mx在u的子树,g[u]=max(g[u],mx’[v]+dis(u,v)),mx’是次大值。到此问题解决。

code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LF double
#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=5*1e5;
LL n,K,gra,sum,to[maxn*2+10],len[maxn*2+10],next[maxn*2+10],
begin[maxn+10],cnt[maxn+10],f[maxn+10],g[maxn+10],dep[maxn+10],
mx[maxn+10],mx2[maxn+10],mxbel[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,LL pre){
    for(LL i=begin[now];i;i=next[i])if(to[i]!=pre){
        dep[to[i]]=dep[now]+len[i];
        dfs(to[i],now);
        cnt[now]+=cnt[to[i]];
        if(cnt[to[i]]){
            if(mx[to[i]]+len[i]>mx[now]){
                mx2[now]=mx[now];
                mx[now]=mx[to[i]]+len[i];
                mxbel[now]=to[i];
            }else mx2[now]=max(mx2[now],mx[to[i]]+len[i]);
            sum+=len[i]*2;
        }
    }
}
void dfss(LL now,LL pre,LL tmp){
    f[now]=dep[now]-dep[tmp];
    for(LL i=begin[now];i;i=next[i])if(to[i]!=pre){
        //g[to[i]]=max(g[now]+len[i],(mxbel[now]!=to[i])?(mx[now]+len[i]):(mx2[now]+len[i]));
        if(mxbel[now]!=to[i])g[to[i]]=max(g[now],mx[now])+len[i];
        else g[to[i]]=max(g[now],mx2[now])+len[i];
        dfss(to[i],now,(cnt[to[i]])?to[i]:tmp);
    }
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%lld%lld",&n,&K);LL u,v,w,st;
    fo(i,2,n){
        scanf("%lld%lld%lld",&u,&v,&w);
        insert(u,v,w);insert(v,u,w);
    }
    fo(i,1,K)scanf("%lld",&u),cnt[u]++,st=u;
    dep[st]=1;dfs(st,0);
    dfss(st,0,st);
    fo(i,1,n)printf("%lld\n",sum+f[i]*2-max(g[i],mx[i]));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值