题意:
有n个节点,n-1条边,其中k个节点为危险节点,有大规模杀伤性武器,切断哪些路能使得这些大规模杀伤性武器的危险节点之间彼此不连通,且切断的边权值之和最小。
思路:
初始化每个节点为一个集合,并记录每个集合中危险节点的数目(0或1)。
要实现权值之和尽可能的小,则要权值尽可能小,故先将n-1条边按权值先升序排序。
排序后枚举这些边:
若边的两端节点所在集合均有大规模杀伤性武器,则删除它并累计其权值。
若只有一边有,则合并这两个集合(用并查集),合并时尽可能将危险节点置于父节点位置。
若没有,则合并这两个集合。
枚举结束,输出权值累计值即可。
ps.很不错的并查集详解:http://blog.youkuaiyun.com/dellaserss/article/details/7724401
code:
/*
* @author Novicer
* language : C++/C
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
#define INF 2147483647
#define cls(x) memset(x,0,sizeof(x))
#define rise(i,a,b) for(int i = a ; i <= b ; i++)
using namespace std;
const double eps(1e-8);
typedef long long lint;
const int maxn = 100000 + 5;
struct Edge{
int u , v , c;
}rd[maxn];
int n,k;
int flag[maxn];
int father[maxn];
bool cmp(Edge a , Edge b){
return a.c > b.c;
}
void init(){
cls(flag);
for(int i = 0 ; i < maxn ; i++){
father[i] = i;
}
}
int find(int x){
if(x != father[x]){
father[x] = find(father[x]);
}
return father[x];
}
void mix(int x ,int y){
father[x] = y;
}
void input(){
init();
cin >> n >> k;
for(int i = 0 ; i < n-1 ; i++){
scanf("%d%d%d",&rd[i].u,&rd[i].v,&rd[i].c);
}
for(int i = 0 ; i < k ; i++){
int tmp;
scanf("%d",&tmp);
flag[tmp] = 1;
}
}
void solve(){
sort(rd , rd+n-1 , cmp);
lint ans = 0 ;
for(int i = 0 ; i < n-1 ; i++){
if(flag[find(rd[i].u)] && flag[find(rd[i].v)])
ans += rd[i].c;
else if(flag[find(rd[i].u)])
mix(find(rd[i].v) , find(rd[i].u));
else
mix(find(rd[i].u) , find(rd[i].v));
}
cout << ans << endl;
}
int main(){
int t ; cin >> t;
while(t--) {
input();
solve();
}
return 0;
}