火山哥周遊世界 (樹形dp)

題意:一共有n個國家,標號1~n,每條邊都有一定的時間花費,任意兩個國家之間兩兩可達。
火山哥一共決定去K個國家。現在他想要知道:如果他從第 i 個國家出發,經過這 K 個國家的最短時間是多少?

輸入:
3 3 (n,k)
1 2 1
2 3 4
1
2
3
輸出:
5
6
5

輸入:
5 2
1 3 1
2 3 2
4 3 3
5 1 4
5
1
輸出:
4
7
5
8
4

解題思路:
出發點與根節點與所有關鍵點構成的子樹邊權總和乘2-出發點到最遠關鍵點的距離

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000010
typedef long long ll;
struct Edge{
	int to,w,nxt;
}edge[maxn];
int n,k,tot=0,vis[maxn],siz[maxn],head[maxn];
ll dis1[maxn],dis2[maxn],sum[maxn];
void add(int u,int v,int w){
	edge[++tot].to=v; edge[tot].w=w; edge[tot].nxt=head[u]; head[u]=tot;
	edge[++tot].to=u; edge[tot].w=w; edge[tot].nxt=head[v]; head[v]=tot;
}
void cmp(ll x,int u){
    if(x>=dis1[u]) dis2[u]=dis1[u],dis1[u]=x;
    else if(x>dis2[u]) dis2[u]=x;
}
void dfs1(int u,int fa){
	if(vis[u]) siz[u]=1;
	sum[u]=dis1[u]=dis2[u]=0;
    for(int i=head[u];~i;i=edge[i].nxt){
    	int to=edge[i].to,w=edge[i].w;
    	if(fa==to) continue;
    	dfs1(to,u);
    	siz[u]+=siz[to];
        sum[u]+=sum[to]+(siz[to]!=0)*2*w;
        cmp(dis1[to]+(siz[to]!=0)*w,u);
	}
}
void dfs2(int u,int fa){
	for(int i=head[u];~i;i=edge[i].nxt){
    	int to=edge[i].to,w=edge[i].w;
    	if(fa==to) continue;
    	sum[to]=sum[u]-2*w*(siz[to]!=0)+(n-siz[to]!=0)*2*w;
    	if(siz[to]!=n){
            if(dis1[to]+w==dis1[u]) cmp(dis2[u]+w,to);
            else cmp(dis1[u]+w,to);
        }
    	dfs2(to,u);
	}
}
int main(){
	memset(vis,0,sizeof(vis));
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&k);
	for(int i=1;i<n;i++){
		int u,v,w; scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
	}
	for(int i=1;i<=k;i++){
		int x; scanf("%d",&x); 
		vis[x]=1;
	}
	dfs1(1,0); dfs2(1,0);
	for(int i=1;i<=n;i++) printf("%lld\n",sum[i]-dis1[i]);
	return 0;
}
發佈了428 篇原創文章 · 獲贊 15 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章