0-1 MST CodeForces - 1242B(并查集)
0-1 MST CodeForces - 1242B(并查集)
题意
给出由边权仅为0,1的边连成的完全图中的1-边,求最小生成树。
思路
显然,给出的完全图,在n的范围上跑一遍最小生成树肯定会TLE,那么就得想别的办法来解决这个问题。由于边权只有0,1,所以目标变为了求只由0-边构成的图中连通块的数量。所以通过并查集,每次和已有的连通块比较,如果当前点与已有连通块间的1-边的边数小于已有连通块的点数,那么必有一条0-边将题目相连,于是可以把该点并入连通块,否则新开连通块。
AC代码
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include<vector>
#include<map>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
vector<int> G[100005],block;
int fa[100005],num[100005];
map<int, int> esum;
int n, m;
int Find(int x)
{
return x == fa[x] ? x : fa[x] = Find(fa[x]);
}
void Merge(int x, int y)
{
int fx = Find(x), fy = Find(y);
if (fx == fy)return;
fa[fy] = fx;
num[fx] += num[fy];
}
int main()
{
int u, v;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
fa[i] = i;
for (int i = 0; i < m; i++)
{
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
for (int i = 1; i <= n; i++)
{
num[i] = 1;
esum.clear();
for (int j = 0; j < G[i].size(); j++)
{
int v = G[i][j];
if (v > i)
continue;
int fv = Find(v);
esum[fv]++;
}
for (int j = 0; j < block.size(); j++)
{
int fx = Find(block[j]), fy = Find(i);
if (fx == fy)
continue;
if (num[fx] > esum[fx])
{
Merge(fx, fy);
}
}
int fx = Find(i);
if (fx == i)
block.push_back(i);
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
if (fa[i] == i)
ans++;
}
printf("%d", ans - 1);
return 0;
}