#4043. 聚会(kamp)

25 篇文章 0 订阅
6 篇文章 0 订阅

题目描述

一颗树 nnn 个点,n−1n-1n−1 条边,经过每条边都要花费一定的时间,任意两个点都是联通的。

有 KKK 个人(分布在 KKK 个不同的点)要集中到一个点举行聚会。

聚会结束后需要一辆车从举行聚会的这点出发,把这 KKK 个人分别送回去。

请你回答,对于 i=1⋯ni=1 \cdots ni=1⋯n,如果在第 iii 个点举行聚会,司机最少需要多少时间把 KKK 个人都送回家
输入格式

第一行两个数,n,Kn,Kn,K。

接下来 n−1n-1n−1 行,每行三个数,x,y,zx,y,zx,y,z 表示 xxx 到 yyy 之间有一条需要花费 zzz 时间的边。

接下来 KKK 行,每行一个数,表示 KKK 个人的分布。
输出格式

输出 nnn 个数,第 iii 行的数表示:如果在第 iii 个点举行聚会,司机需要的最少时间。
样例
样例输入

7 2
1 2 4
1 3 1
2 5 1
2 4 2
4 7 3
4 6 2
3
7

样例输出

11
15
10
13
16
15
10

数据范围与提示

K≤N≤500000 K \le N \le 500000 K≤N≤500000

1≤x,y≤N,1≤z≤1000000 1 \le x,y \le N, 1 \le z \le 1000000 1≤x,y≤N,1≤z≤1000000
来源

coci 2015
题解:
首先,这道题对于每个点都需要求,正常人都会想到树形DP,又好求又可以保存状态。但树形DP一个很重要的要求就是起点和终点都得是当前节点。这就有点麻烦。
所以可以想出,我们不仅需要从当前节点遍历完所有需要的节点。还需要求出以当前节点为起点的最长链。
细节很多。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 500005
using namespace std;
long long f[N],g[N],c[N][2],up[N],n,tot,edge[N<<1],k;
int head[N],ver[N<<1],nex[N<<1],s[N],biao[N];
int v[N];int root=1;
inline void add(int x,int y,long long z){
	nex[++tot]=head[x];
	head[x]=tot;ver[tot]=y;edge[tot]=z;
}
void dfs1(int x){
	v[x]=1;
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(v[y])continue;
		dfs1(y);
		if(s[y])f[x]+=f[y]+2*edge[i];
		s[x]=s[x]+s[y];
	}
	v[x]=0;
}
void dfs2(int x){
	v[x]=1;
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(v[y])continue;
		if(s[y]!=k)
		if(s[y])g[y]=g[x]+f[x]-f[y];
		else g[y]=g[x]+f[x]-f[y]+2*edge[i];
		dfs2(y);
	}
	v[x]=0;
}
void modify(int x,long long w){
	if(c[x][0]<w)c[x][1]=c[x][0],c[x][0]=w;
	else if(c[x][1]<w) c[x][1]=w;
}
void dfs3(int x){
	v[x]=1;
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(v[y])continue;
		dfs3(y);
		if(s[y])modify(x,c[y][0]+edge[i]);//若无节点就无最长链
	}
	v[x]=0;
}
void dfs4(int x){
	v[x]=1;
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(v[y])continue;
		up[y]=max(up[y],up[x]+edge[i]);
		if(c[x][0]==c[y][0]+edge[i])up[y]=max(up[y],c[x][1]+edge[i]);
		else up[y]=max(up[y],c[x][0]+edge[i]);
		dfs4(y);
	}
	v[x]=0;
}
		
int main()
{
	cin>>n>>k;
	for(int i=1;i<n;i++){
		int x,y;long long z;
		cin>>x>>y>>z;
		add(x,y,z);add(y,x,z);
	}
	for(int i=1;i<=k;i++){
		int x;cin>>x;
		s[x]=1;
	}
	dfs1(root);
	dfs2(root);
	dfs3(root);
	dfs4(root);
	for(int i=1;i<=n;i++){
		cout<<f[i]+g[i]-max(c[i][0],up[i])<<endl;
	}
	return 0;
}
/*
7 2
1 2 4
1 3 1
2 5 1
2 4 2
4 7 3
4 6 2
3
7
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值