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;
}

猜你喜欢

转载自www.cnblogs.com/grimcake/p/9575327.html