题意
https://vjudge.net/problem/CodeForces-1243D
有一张完全图,n个节点
有m条边的边权为1,其余的都为0
这m条边会给你
问你这张图的最小生成树的权值
思路
很简单的思路就是将权值为0的边构成的子图缩成若干个连通块,那么答案就是连通块个数-1了。
但是,边数太多了,有n*(n-1)/2-m条权值为0的边。
考虑对于每一个点,计算这个点到其连的点所在集合的边数,用map存,然后和对应集合大小(点数)比较,如果前者小于后者,那么以为着这个点到这个集合还有一些权值为0的边,所以可以将这个点加入到这个集合。
注意合并的时候想清楚是这个点合并到当前集合,不要写反了!不然TLE12。
最后答案就是连通块个数减1了。
这里我们可以将无向图转成有向图,让每个点只与比它小的点连边,这样遍历的时候就只用考虑比这个点小的点的情况,节约时间。
代码
#include<bits/stdc++.h> using namespace std; #define inf 0x3f3f3f3f #define ll long long const int N=200005; const int mod=1e9+7; const double eps=1e-8; const double PI = acos(-1.0); #define lowbit(x) (x&(-x)) vector<int> g[N]; int f[N],sz[N]; int find(int x) { if(x==f[x]) return x; return f[x]=find(f[x]); } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) f[i]=i,sz[i]=1; for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); if(u<v) //搜索有序化 swap(u,v); g[u].push_back(v); } vector<int> h; for(int i=1;i<=n;i++) { map<int,int> mp; for(int j:g[i]) mp[find(j)]++; for(int j:h) { int fi=find(i),fj=find(j); if(fi!=fj&&mp[fj]<sz[fj]) { f[fi]=fj; sz[fj]+=sz[fi]; } } if(find(i)==i) h.push_back(i); } int ans=0; for(int i=1;i<=n;i++) { if(find(i)==i) ans++; } printf("%d\n",ans-1); return 0; }