jzoj 2936_逐个击破_并查集

lzusa 发布于 2017-09-08 2 次阅读


题目描述

三大战役的平津战场上,傅作义集团在以北平、天津为中心,东起唐山西至张家口的铁路线上摆起子一字长蛇阵,并企图在溃败时从海上南逃或向西逃窜。为了就地歼敌不让其逃走,maozedong制定了先切断敌人东洒两头退路然后再逐个歼灭敌人的战略方针。 秉承伟大军事家的战略思想,作为一个有智慧的军长你,遇到了一个类似的战场局面: 现在有N个城市,其中K个被敌方军团占领了,N个城市间有N-1条公路相连,破坏其中某条公路的代价是已知的,现在,告诉你K个敌方军团所在的城市,以及所有公路破坏的代价,请你算出花费最少的代价将这K个地方军团互相隔离开,以便第二步逐个击破敌人。


 

思路

最大生成树,可以用一个数组a来判断每一个并查集里面有几个是被占领的城市,在合并集合时转移即可


 

 

#include 
#include 
using namespace std;
#define maxn 100005
struct edge
{
    int x, y, w;
}e[maxn];
int f[maxn], a[maxn];
int cmp(edge a, edge b)
{
    return a.w > b.w;
}
int find(int x)
{
    if (f[x] == x) return x;
    f[x] = find(f[x]);
    return f[x];
}
inline int read()
{
    int x=0,p=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-')p=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*p;
}
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++)
        f[i] = i;
    for (int i = 1; i <= m; i++)
    {
        int x = read();
        a[x] = 1;
    }
    double ans = 0;
    for (int i = 1; i < n; i++)
    {
        int x, y, w;
        scanf("%d%d%d", &x, &y, &w);
        e[i] = (edge) {x, y, w};
        ans += w;
    }
    sort(e + 1, e + n, cmp);
    for (int i = 1; i < n; i++)
    {
        int xx = find(e[i].x), yy = find(e[i].y);
        if (a[xx] + a[yy] < 2)
        {
            f[xx] = yy;
            a[yy] += a[xx];
            a[xx] = 0;
            ans -= e[i].w;
        }
    }
    printf("%.lf\n", ans);
}

 

]]>