2012 Multi-University Training Contest 2
HDU-4314
题意:给定一棵树,每条边有权值,要求删边使得树分为k个部分,有k个特殊的节点,要使这k个节点在不同的部分,求删除的边的最小权值和
题解:边从大到小排序,并查集维护,如果该条边的两个顶点所在的集合有特殊点,则删除这条边,如果只有一个特殊点,合并且让特殊点作为根节点
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct Edge{ int u, v, next; long long w; }edge[200010]; int head[100010], cnt; int vis[100010]; int n, k; int fa[100010]; int Find(int x){ return x == fa[x]?x:fa[x] = Find(fa[x]); } void init(){ memset(head, -1, sizeof(head)); memset(vis, 0, sizeof(vis)); cnt = 0; for(int i = 0; i<=n; i++) fa[i] = i; } void add(int u, int v, long long w){ edge[cnt].u = u; edge[cnt].v = v; edge[cnt].w = w; edge[cnt].next = head[u]; head[u] = cnt++; } bool cmp(Edge a, Edge b){ return a.w>b.w; } int main(){ int T; scanf("%d", &T); while(T--){ scanf("%d %d", &n, &k); init(); for(int i = 1; i<=n-1; i++){ int x, y; long long z; scanf("%d %d %lld", &x, &y, &z); add(x, y, z); //add(y, x, z); } for(int i = 1; i<=k; i++){ int x; scanf("%d", &x); vis[x] = 1; } sort(edge, edge+(n-1), cmp); long long ans = 0; int num = 1; for(int i = 0; i<n-1; i++){ int a = edge[i].u, b = edge[i].v; int roota = Find(a), rootb = Find(b); if(vis[roota] == 1 && vis[rootb] == 1){ ans += edge[i].w; num++; } if(vis[roota] == 1 && vis[rootb]!=1){ fa[rootb] = roota; } if(vis[rootb] == 1 && vis[roota]!=1){ fa[roota] = rootb; } if(vis[roota] != 1 && vis[rootb]!=1){ fa[rootb] = roota; } if(num == k) break; } printf("%lld\n", ans); } return 0; }