求出单独连通块的数量

1.利用循环成环来找联通块

思路分析:

  1. 构建图

    • 将数组 a和 b 视为图中的边,其中 a[i] 指向 b[i]。
    • 用一个映射 mp 来表示这个图,其中 mp[a[i]] = b[i]
  2. 寻找环

    • 使用一个布尔映射 flag 来记录每个节点是否已经访问过。
    • 遍历每个节点,找到所有环,并标记环中的所有节点。
    • 每找到一个新的环,计数器 cnt 增加 1。

详细步骤

  1. 输入和初始化

    • 读取两个数组 a和 b 的值,并初始化 mpflag
  2. 构建映射

    • 将数组 a 和 b 映射到 mp 中,使得 mp[a[i]] = b[i]
  3. 遍历并寻找环

    • 遍历每个节点,如果节点未被访问过,则从该节点开始,使用循环找到环并标记环中的所有节点。
    • 对于每一个新的环,计数器 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,它们分别表示图中的边,我们的目标是计算有多少个连通分量。

思路分析

  1. 并查集的初始化

    • 使用一个数组 p 来表示并查集,初始时每个节点指向自己,表示每个节点都是独立的集合。
  2. 读入数据并构建并查集

    • 读取数组 a 和 b 的值。
    • 对于每一对 a[i] 和 b[i],将它们在并查集中合并,表示它们是连通的。
  3. 找到所有连通分量

    • 遍历每个节点,通过 find 函数找到每个节点所属的连通分量的根节点,并将根节点插入集合 s 中。
    • 集合 s 的大小即为连通分量的数量。

详细步骤

  1. 并查集函数

    • find(x):用于查找节点 x 所属的连通分量的根节点,并进行路径压缩以加速后续操作。
  2. 输入和初始化

    • 读取测试案例的数量 T
    • 对于每个测试案例,读取 n 以及数组 a 和 b 的值,并初始化并查集。
  3. 合并操作

    • 对于每一对 a[i] 和 b[i],将它们合并到同一个连通分量中。
  4. 计算连通分量数量

    • 使用集合 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值