正解:利用并查集统计所有的信仰,一开始先把所有人自己设为一种信仰,即 fa[i] = i;
,然后读入两个同样信仰的人,若这两人的信仰不同,则合并为同一个信仰,即 fa[fx] = fy;
或 fa[fy] = fx;
,最后统计有多少人 fa[i]
还是等于 i
,输出这种人的个数,也就是信仰的种类个数。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m,a,b,ans;
int fa[50010];
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);
fa[fx] = fy;
}
void solve()
{
cin >> n >> m;
for (int i = 1;i <= n;i++) fa[i] = i;
for (int i = 1;i <= m;i++)
{
cin >> a >> b;
int fx = find(a),fy = find(b);
if (fx != fy) merge(fx,fy);
}
for (int i = 1;i <= n;i++) if (i == fa[i]) ans++;
cout << ans;
}
signed main()
{
solve();
return 0;
}
输入:5 2 4 2 3 1
输出:3
正解:
把每个同学都看成点,A同学将信息传给B同学,就相当于在A和B之间建立了一条有向条边。
将其加入并查集中,当遇到两个点的祖先节点相同时,则说明他们已经在同一个集合,那么就能构成环,此时判断一下环的长度即可。
这里要用一个 d[]
数组,保存第i个节点到其祖先节点的距离。
A和B所在集合构成环的长度就是 d[a]+d[b]+1
。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n;
int ffa[200010],fa[200010];
int a[200010];
int mi = 2e9;
int find(int x) {return x == ffa[x] ? x : ffa[x] = find(ffa[x]);}
void solve()
{
cin >> n;
for (int i = 1;i <= n;i++)
cin >> a[i],ffa[i] = fa[i] = i;
for (int i = 1;i <= n;i++)
{
int fx = find(i),fy = find(a[i]);
if (fx == fy)
{
int ans = 1;
for (int j = a[i];j-i;j = fa[j]) ans++;
mi = min(mi,ans);
}
ffa[fx] = fy;
fa[fx] = a[i];
}
cout << mi;
}
signed main()
{
solve();
return 0;
}