2020杭电多校(二) Total Eclipse(并查集)

题目想问的是每次取一个所能取到最大的连通块,并把他们的上面每个点的权值-1,最少需要多少次操作

这题顺着做,就是每次找到最大的连通块,然后-1,之后可能出现某些连接点权值为0断开,变成多个连通块继续做。

因此考虑倒着做,我们发现,每个点作为单独的连通块做出的贡献就是当旁边的点权值为0了,因此将点的权值排序后

从头枚举,对于每个点枚举他的邻边,如果邻点的权值大于他,就把他们两个集合合并,答案的贡献就是两个集合父节点的权值差

之后枚举最后父节点是自己的答案加上即可。

#include
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int mod=998244353;
vector<int> g[N];
int b[N];
int p[N];
vector<int> now;
int sign[N];
bool cmp(int x,int y){
    return b[x]>b[y];
}
int find(int x){
    if(x!=p[x]){
        p[x]=find(p[x]);
    }
    return p[x];
}
int main(){
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        int i;
        now.clear();
        for(i=1;i<=n;i++){
            cin>>b[i];
            g[i].clear();
            p[i]=i;
            now.push_back(i);
            sign[i]=0;
        }
        while(m--){
            int x,y;
            cin>>x>>y;
            g[x].push_back(y);
            g[y].push_back(x);
        }
        sort(now.begin(),now.end(),cmp);
        ll ans=0;
        for(auto x:now){
            for(i=0;i){
                int y=g[x][i];
                if(b[y]<b[x])
                    continue;
                int pa=find(x);
                int pb=find(y);
                if(pa!=pb){
                    p[pb]=pa;
                    ans+=b[pb]-b[pa];
                }
            }
        }
        for(i=1;i<=n;i++)
            if(p[i]==i)
                ans+=b[i];
        cout<endl;
    }
    return 0;
}
View Code

 

你可能感兴趣的:(2020杭电多校(二) Total Eclipse(并查集))