1.利用循环成环来找联通块
思路分析:
-
构建图:
- 将数组 a和 b 视为图中的边,其中 a[i] 指向 b[i]。
- 用一个映射
mp
来表示这个图,其中mp[a[i]] = b[i]
。
-
寻找环:
- 使用一个布尔映射
flag
来记录每个节点是否已经访问过。 - 遍历每个节点,找到所有环,并标记环中的所有节点。
- 每找到一个新的环,计数器
cnt
增加 1。
- 使用一个布尔映射
详细步骤
-
输入和初始化:
- 读取两个数组 a和 b 的值,并初始化
mp
和flag
。
- 读取两个数组 a和 b 的值,并初始化
-
构建映射:
- 将数组 a 和 b 映射到
mp
中,使得mp[a[i]] = b[i]
。
- 将数组 a 和 b 映射到
-
遍历并寻找环:
- 遍历每个节点,如果节点未被访问过,则从该节点开始,使用循环找到环并标记环中的所有节点。
- 对于每一个新的环,计数器
cnt
增加 1。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=4e5+10;
int n;
int a[N];
int b[N];
map<int,int>mp;
map<int,bool>flag;
const int mod=1e9+7;
void solve()
{
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
}
for(int i=0;i<n;i++){
cin>>b[i];
}
for(int i=0;i<n;i++){
mp[a[i]]=b[i];
}
int cnt=0;
for(int i=1;i<=n;i++)
{
if(flag[i]==false)
{
flag[i]=true;
int s=i;
int ne=mp[s];
flag[ne]=true;
while(ne!=s)
{
ne=mp[ne];
flag[ne]=true;
}
cnt++;
}
}
int ans=1;
for(int i=0;i<cnt;i++){
ans=ans*2%mod;
}
cout<<ans<<"\n";
mp.clear();
flag.clear();
}
signed main()
{
cin.tie(0); cout.tie(0);
ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
2.利用并查集来实现找到单独的连通块
这个问题是一个经典的使用并查集(Union-Find)来解决的图论问题。给定两个数组 a 和 b,它们分别表示图中的边,我们的目标是计算有多少个连通分量。
思路分析
-
并查集的初始化:
- 使用一个数组
p
来表示并查集,初始时每个节点指向自己,表示每个节点都是独立的集合。
- 使用一个数组
-
读入数据并构建并查集:
- 读取数组 a 和 b 的值。
- 对于每一对 a[i] 和 b[i],将它们在并查集中合并,表示它们是连通的。
-
找到所有连通分量:
- 遍历每个节点,通过
find
函数找到每个节点所属的连通分量的根节点,并将根节点插入集合s
中。 - 集合
s
的大小即为连通分量的数量。
- 遍历每个节点,通过
详细步骤
-
并查集函数:
find(x)
:用于查找节点 x 所属的连通分量的根节点,并进行路径压缩以加速后续操作。
-
输入和初始化:
- 读取测试案例的数量
T
。 - 对于每个测试案例,读取 n 以及数组 a 和 b 的值,并初始化并查集。
- 读取测试案例的数量
-
合并操作:
- 对于每一对 a[i] 和 b[i],将它们合并到同一个连通分量中。
-
计算连通分量数量:
- 使用集合
s
来记录所有不同的连通分量的根节点。 - 集合
s
的大小即为连通分量的数量。
- 使用集合
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=4e5+10;
int n;
int a[N];
int b[N];
const int mod=1e9+7;
int p[N];
set<int>s;
int find(int x)
{
if(p[x]!=x) p[x]=find(p[x]);
return p[x];
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++) p[i]=i;
for(int i=0;i<n;i++){
cin>>a[i];
}
for(int i=0;i<n;i++){
cin>>b[i];
}
for(int i=0;i<n;i++)
{
int x=find(a[i]);
int y=find(b[i]);
p[x]=y;
}
for(int i=1;i<=n;i++){
s.insert(find(i));// 并查集的信息都存在祖宗节点上
}
int cnt=s.size();
int ans=1;
for(int i=0;i<cnt;i++){
ans=ans*2%mod;
}
cout<<ans<<"\n";
s.clear();
}
signed main()
{
cin.tie(0); cout.tie(0);
ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}