Connected Component 并查集


Description

Driver Fang is given NN nodes, each node is labeled with an integer between 11 and 10000001000000 (inclusive and labels are not necessarily distinct). Two nodes have an edge between them, if and only if the GCD (Greatest Common Divisor) of the labels of these nodes is greater than 11. Now, his task is to count the number of connected components in the graph.

Input

First line of the input TT (TT ≤ 10) denotes the number of cases. Then TT cases follow. Each case consists of 2 lines. The  first line has a number NN (1 ≤ NN ≤ 100000) denoting the number of nodes. The next line consists of NN numbers. The ii-th (1 ≤ ii ≤ n) number XiXi (1 ≤ XiXi ≤ 1000000) denotes the label of the node ii.

Output

For each case you have to print a line consisting consisting the case number followed by an integer which denotes the number of connected components. Look at the output for sample input for details.

Sample Input

2
3
2 3 4
6 
2 3 4 5 6 6

Sample Output

Case 1: 2
Case 2: 2


题目大意,给出n个数,如果任意两个数的gcd >1那么他们之间有边相连,求联通块的个数。

题解:很明显的并查集,关键在于怎么判断两个数之间的gcd>1,如果直接用n^2的枚举算法,显然会超时。因此从他们的素因子入手。

可以这样想,把他们每个数的素因子分解出来,并把这个数和它们的素因子加入到一个并查集里去。数的大小是10^6规模,因此我们检测到10^3就够了

也就是检测1000以内的素数(因为如果存在1000以外的素因子y,那么肯定也存在1000以内的素因子x使得xy*...=num,因此我们可以通过求x的时候顺便把y求出来)

1000以内的素数只有168个。

核心就是:

void init()
{
	for(int i = 1;i <= MAX;i++) parent[i] = i;
	for(int i = 0;i < n;i++)
	{
		int tmp = arr[i];
		for(int j = 0;j < pcnt;j++)
		{
			while(tmp%primes[j] == 0){
				join(primes[j],arr[i]);
				tmp /= primes[j];
				if(tmp != 1) join(arr[i],tmp);
				else break;
			}
		}
		if(tmp > 1) join(tmp,arr[i]);
	}
}


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;
const int MAX = 1e7;
int arr[MAX+10];
int parent[MAX+5];
int n;
int primes[10005];
int pcnt;
int cccc[MAX];
void getprimes()
{
	primes[pcnt++] = 2;
	for(int i = 3;i <= 2000;i++)
	{
		int flag = 0;
		for(int j = 2;j < i;j++)
		{
			if(i%j == 0)
			{
				flag = 1;
				break;
			}
		}
		if(!flag)
		{
			primes[pcnt++] = i;
		}
	}
	//cout<<pcnt<<endl;
}
int find(int x) {
    if (x != parent[x]) parent[x] = find(parent[x]);
    return parent[x];
}
void join(int x1,int x2)
{
	int p1 = find(x1);
	int p2 = find(x2);
	if(p1 != p2) parent[p1] = p2;
}
void init()
{
	for(int i = 1;i <= MAX;i++) parent[i] = i;
	for(int i = 0;i < n;i++)
	{
		int tmp = arr[i];
		for(int j = 0;j < pcnt;j++)
		{
			while(tmp%primes[j] == 0){
				join(primes[j],arr[i]);
				tmp /= primes[j];
				if(tmp != 1) join(arr[i],tmp);
				else break;
			}
		}
		if(tmp > 1) join(tmp,arr[i]);
	}
}
int main()
{
	getprimes();
	int cas = 0;
	int T;scanf("%d",&T);
	while(T--)
	{
		memset(cccc,0,sizeof(cccc));
		scanf("%d",&n);
		for(int i = 0;i < n;i++) scanf("%d",&arr[i]);
		init();
		int ans = 0;
		for(int i = 0;i < n;++i) 
		{
			/*
			if(arr[i] == 1){
				ans++;
				continue;
			}
			*/
			if(cccc[find(arr[i])] == 0) ans++;
			cccc[find(arr[i])] = 1;
		}
		printf("Case %d: %d\n",++cas,ans);
	}
	return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值