Codeforces D

D - The Child and Zoo

思路:

并查集+贪心

每条边的权值可以用min(a[u],a[v])来表示,然后按边的权值从大到小排序

然后用并查集从大的边开始合并,因为你要合并的这两个联通块之间的点肯定要经过这条边,而这条要合并的边是所有已经合并中的最小的,所以两个联通块之间的所有点之间的f就是这条边(而且是所有情况最大的,因为是从最大的边开始贪心的)。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))

const int N=1e5+5;
struct edge{
    int u,v,w;
    bool operator < (edge t){
        return w>t.w;
    }
}edge[N];
int cnt[N];
int rnk[N];
int par[N];
int a[N];
void init(int n){
    for(int i=0;i<=n;i++)par[i]=i,cnt[i]=1;
}
int find(int x){
    if(x==par[x])return x;
    else return par[x]=find(par[x]);
}
void unite(int x,int y){
    int px=find(x);
    int py=find(y);
    if(px!=py){
        if(rnk[px]<rnk[py]){
            par[px]=py;
            cnt[py]+=cnt[px];
        }
        else{
            if(rnk[px]==rnk[py]){
                rnk[px]++;
            }
            par[py]=px;
            cnt[px]+=cnt[py];
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m,u,v;
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    int c=0;
    while(m--){
        cin>>u>>v;
        edge[c].u=u;
        edge[c].v=v;
        edge[c++].w=min(a[u],a[v]);
    }
    sort(edge,edge+c);
    init(n);
    ll ans=0;
    for(int i=0;i<c;i++){
        int pu=find(edge[i].u);
        int pv=find(edge[i].v);
        if(pu!=pv){
            ans+=(ll)edge[i].w*cnt[pu]*cnt[pv];
            unite(edge[i].u,edge[i].v);
        }
    }
    cout<<fixed<<setprecision(6)<<ans*2.0/(1.0*(n-1)*n)<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/widsom/p/8366282.html