HDU6763 Total Eclipse(并查集)

问题描述

拜特兰有n个城市和m条双向道路。这些城市被标记为1,2,…,n,第i个城市的亮度是bi。

 

魔术师Sunset想和拜特兰开个玩笑,制造一个月全食,使每个城市的亮度变为零。Sunset可以执行以下操作任意次数:

 

·选择一个整数k(1≤k≤n)。

 

·选择k个不同的城市c1,c2,…,ck(1≤ci≤n),使它们相互连接。换言之,对于每一对不同选择的城市ci和cj(1≤i<j≤k),如果您在city ci,您可以到达city cj,而不必访问{c1,c2,…,ck}以外的城市。

 

·对于每个选定的城市ci(1≤i≤k),将bci降低1。

 

请注意,Sunset将始终选择具有最大可能值的k。现在Sunset想知道他需要做的最小操作数是多少,请编写一个程序来帮助他。

 


输入

输入的第一行包含单个整数T(1≤T≤10),即测试用例的数量。

 

对于每种情况,输入的第一行包含两个整数n和m(1≤n≤100000,1≤m≤200000),表示城市数量和道路数量。

 

输入的第二行包含n个整数b1,b2,…,bn(1≤bi≤109),表示每个城市的亮度。

 

以下每一条m线包含两个整数ui和vi(1≤ui,vi≤n,ui≠vi),表示第ui个城市和第vi个城市之间的一条双向道路。请注意,同一对城市之间可能有多条道路。

 


输出

对于每个测试用例,输出一行包含一个整数,即最小操作数。

题解:

每次一定是选择一个极大连通块,将里面所有数同时减小,直到最小值变成0,然后把变成0的点删掉,分裂成多个连通块继续。

为了实现这个策略,可以将整个过程倒过来看,先将所有点按照b的值从大到小排序,维护一个集合,依次将所有数加入到集合中。加入的时候遍历所有与x相令的点y,如果y在x之前加入且x和y不连通,则将x和y合并,并将y所在连通块的树根设为x。

每个x在成为最小值之前已经被做了b[pre[x]]次操作,所以每个点对答案的贡献是b[x]-b[pre[x]]

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
typedef long long ll;
vector<int> g[maxn];
int b[maxn];
int a[maxn];
int father[maxn];
int vis[maxn];
int findfather (int x) {
    int a=x;
    while (x!=father[x]) x=father[x];
    while (a!=father[a]) {
        int z=a;
        a=father[a];
        father[z]=x;
    }
    return x;
}

bool cmp (int x,int y) {
    return b[x]>b[y];
}
int pre[maxn];
int t,n,m;
int main () {
    scanf("%d",&t);
    while (t--) {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++) {
            g[i].clear();
            father[i]=i;
            pre[i]=0;
            vis[i]=0;
            scanf("%d",b+i);
            a[i]=i;
        }
        sort(a+1,a+n+1,cmp);
        for (int i=1;i<=m;i++) {
            int x,y;
            scanf("%d%d",&x,&y);
            g[x].push_back(y);
            g[y].push_back(x);
        }
        ll ans=0;
        for (int i=1;i<=n;i++) {
            vis[a[i]]=1;
            for (int v:g[a[i]]) {
                if (!vis[v]) continue;
                int faV=findfather(v);
                if (faV==a[i]) continue;
                pre[faV]=father[faV]=a[i];
             }
        }
        for (int i=1;i<=n;i++) ans+=b[i]-b[pre[i]];
        printf("%lld\n",ans);
    }
    return 0;
} 

 

posted @ 2020-09-17 16:05  zlc0405  阅读(209)  评论(0编辑  收藏  举报