并查集 [Total Eclipse]

并查集 Total Eclipse

题解:

官方题解。

自己看完题解的整理:

这个题目明显就是选一个连通块,然后减去这个连通块的最小值,所以变成0的节点要删除,就会分裂成多个连通块。

实现方法就是对b进行排序,从大到小放,每次排序遍历一次这个点 x 的所有的边,对于(x,y) ,如果y在x之前放进去并且y和x没有联通,那么就把x放到y这棵树的根节点的父亲节点,最后答案就是这棵树的儿子节点的b和父亲节点的b的差值,接下来说一下为什么这样是对的。

因为我们首先放进去的是b大的值,这个是最后删去的,按照b从大到小放进去,每次放入一个x,增加一条边(x,y)

  • 那么如果y之前放进去过,并且他的根节点就是本身,是不是说明这个x点是y所在连通块中y连到的数值最大的一个点,如果删去了这个x点,那么y就变成了一个孤立的点(因为这个x点是数值最大的点,即使y连了其他的点,那么数值也没有大于x这个点,所以当x点删去,其他点肯定已经删去了),那么此时删去y这个点的代价就是 y-x。

  • 如果y之前放进去过,并且它的根节点不是他本身,那么就说明这个x点是y所在联通块中一个普通的点,但是他肯定对于另一个点v来说是一个最大的点,所以x对y的影响不大,但是对v来说影响很大,这个v就是当前这个y所在节点的根节点。举个例子:假设v和y已经连号了,v是y的根节点,说明b[v]<b[y] ,那么加入这个x,b[x]<b[v] ,假设x被删除,那么再删去v这个节点的代价就是 b[v]-b[x] 。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
int head[maxn],to[maxn<<1],nxt[maxn<<1],cnt;
void add(int u,int v){
    ++cnt,to[cnt]=v,nxt[cnt]=head[u],head[u]=cnt;
    ++cnt,to[cnt]=u,nxt[cnt]=head[v],head[v]=cnt;
}
int f[maxn];

int findx(int x){
    return x == f[x]?x:f[x]=findx(f[x]);
}

void unite(int x,int y){
    x = findx(x);
    y = findx(y);
    if(x==y) return ;
    f[y]=x;
}

bool same(int x,int y){
    return findx(x)==findx(y);
}

int b[maxn],p[maxn];
void init(int n) {
    cnt = 0;
    for (int i = 0; i <= n; i++) head[i] = 0, f[i] = i, p[i] = i;
}

bool cmp(int i,int j){
    if(b[i]==b[j]) return i<j;
    return b[i]>b[j];
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m;
        scanf("%d%d",&n,&m);
        init(n);
        for(int i=1;i<=n;i++) scanf("%d",&b[i]);
        for(int i=1;i<=m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
        }
        sort(p+1,p+1+n,cmp);
        ll ans=0;
        for(int i=1;i<=n;i++){
            int u = p[i];
            ans+=b[u];
            for(int j=head[u];j;j=nxt[j]){
                int v = to[j];
                //如果没有放进去就continue
                if(b[v]<b[u]) continue;
                if(b[u]==b[v]&&v>u) continue;

                if(!same(u,v)){
                    unite(u,v);
                    ans-=b[u];
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/EchoZQN/p/13368833.html