[CF1242B] 0-1 MST - 并查集

有一张完全图,\(n\) 个节点,有 \(m\) 条边的边权为 \(1\),其余的都为 \(0\),这 \(m\) 条边会给你。问你这张图的最小生成树的权值。

Solution

\(1\) 边视为不存在,那么最后的答案就是 \(0\) 边形成的连通块数 \(-1\)

顺序扫描所有点,对于点 \(i\),枚举由 \([1,i-1]\) 已经形成的集合 \(j\),如果 \(i\)\(j\) 连的边数小于 \(j\) 的大小,那么就表明一定有 \(0\) 边,于是将 \(i\) 所在集合与集合 \(j\) 合并

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 500005;

int n,m,t1,t2,fa[N],sz[N],bel[N];
vector <int> g[N];

int find(int p) {
    return p==fa[p] ? p : fa[p]=find(fa[p]);
}

void merge(int p,int q) {
    p=find(p); q=find(q);
    if(p!=q) {
        fa[p]=q;
        sz[q]+=sz[p];
    }
}

signed main() {
    ios::sync_with_stdio(false);
    vector <int> st;
    cin>>n>>m;
    for(int i=1;i<=m;i++) {
        cin>>t1>>t2;
        g[max(t1,t2)].push_back(min(t1,t2));
    }
    for(int i=1;i<=n;i++) {
        fa[i]=i;
        sz[i]=1;
    }
    for(int i=1;i<=n;i++) {
        map<int,int> cnt;
        for(int j:g[i]) cnt[find(j)]++;
        for(int j:st) {
            if(find(i)==find(j)) continue;
            if(cnt[find(j)]<sz[find(j)]) merge(i,j);
        }
        if(find(i)==i) st.push_back(i);
    }
    int ans=0;
    for(int i=1;i<=n;i++) if(find(i)==i) ++ans;
    cout<<ans-1;
}

猜你喜欢

转载自www.cnblogs.com/mollnn/p/12564108.html
MST