开车


小S在的城镇是一棵树,有\(n?1\)条道路连接了\(n\)个节点,经过每条路需要花一定时间。

现在,小S车上有\(K\)个乘客,第\(i\)个想去的地方是\(a_i\)。(保证\(a_i\)互不相同)

现在,小S想要知道,如果他从每个点出发,载着这\(K\)个人,把他们送到他们各自想到的地方,所需的最少时间是多少。(注意,小S不必再回到起点)。

输入格式
第一行两个整数\(n,K\)

接下来\(n?1\)行,每行一个整数\(x,y,w\),分别表示道路连接的节点和经过所需的时间。节点标号从1开始。

接下来\(K\)行,每行一个整数表示\(a_i\)

输出格式
输出i行,第i行表示小S从\(i\)号节点出发的答案。

样例1
input

5 2
2 5 1
2 4 1
1 2 2
1 3 2
4
5

output

5
3
7
2
2

样例2
input

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

output

11
15
10
13
16
15
10

数据范围
对于50%的数据,\(1≤N≤2000\)
对于100%的数据,\(1≤N≤5×10^5,1≤K≤N\),边的权值\(≤10^6\)

时间限制:1S

空间限制:256MB

如果给题目加一个条件:送完所有人还要回到根节点,那要怎么做呢?其实答案就是将根节点和所有人要到的点连起来的总边权和乘2.我们求出一个根的之后,走一条边,如果这条边属于原来的边集,那么答案不变。否则答案增加走的这条边的两倍。同时如果走了这条边后从\(u\)到达\(v\),以v为根是\(u\)这颗子树上没有人去,答案减去这条边的两倍。

但是这个条件不存在,所以我们还要减去距离根节点最远的点离根节点的距离。所以我们还要在换根法时还要处理出离这个点最远的点。把点分为子树内的和子树外的,子树内的通过树形dp求出,子树外的要在换根法时求出。那么从u走到v时,首先在不和\(u\)这颗子树的就在换根法中求下来,还有就在\(u\)这颗子树的,定义\(dp_i\)\(i\)的子树内最远点的距离。。如果\(dp_u=dp_v+1\),那么就要取不严格次大值。否则就取最大值,更新换根法中的子树外最远的点。

#include
#include 
#include
using namespace std;
const int N=5e5+5;
int n,k,x,y,z,hd[N],dp[N],f[N],p,sz[N];
long long ret,ans[N];
struct edge{
	int v,nxt,w;
}e[N<<1];
void add_edge(int x,int y,int w,int z)
{
	e[z]=(edge){y,hd[x],w};
	hd[x]=z;
}
void dfs(int x,int y)
{
	for(int i=hd[x];i;i=e[i].nxt)
	{
		if(e[i].v!=y)
		{
			dfs(e[i].v,x);
			if(sz[e[i].v])
				sz[x]+=sz[e[i].v],ret+=e[i].w<<1;
			p=dp[e[i].v]+e[i].w,z=f[e[i].v]+e[i].w;
			if(p>dp[x]) 
				f[x]=dp[x],dp[x]=p;
			else if(p>f[x])
				f[x]=p;
			else if(z>f[x])
				f[x]=z;
		}     
	}
}
void sou(int x,int y,long long z,int s)
{  
	ans[x]=z-max(s,dp[x]);
	for(int i=hd[x];i;i=e[i].nxt)
	{
		if(e[i].v!=y)
		{
			if(sz[e[i].v]==k)
				p=-1;
			else if(!sz[e[i].v])
				p=1;
			else
				p=0;
			if(dp[e[i].v]+e[i].w==dp[x])
				sou(e[i].v,x,z+p*e[i].w*2,max(f[x],s)+e[i].w);
			else
				sou(e[i].v,x,z+p*e[i].w*2,max(dp[x],s)+e[i].w);
		}
	}
}
int main()
{
	memset(dp,-9,sizeof(dp));
	memset(f,-9,sizeof(f));
	scanf("%d%d",&n,&k);
	for(int i=1;i