目录
一. 问题描述
Problem Description
给出n个数字,两个数字间只要最大公约数大于1,则画为一组(比如2和6,3和6,那么2和3也在一组),求这些数字能画为多少组。
二. 题解代码
要是每输入一组数据就双重循环判断两两间是否最大公约数>1,则一定超时。因为输入的n个数字大小不一定(不是从一开始的),所以这里把,每个数字的所有质因数分解到一个他的数组里,那么他和他自己的所有质因数肯定都是一个组合里的,每输入到一个数字就合并他和他的所有质因数(因为这里给出的数字不一定多大),这样所有输入的数字都会最终划分到不同的圈子。对于如何预处理质因数,其实跟欧拉函数思路差不多,其实就是统计倍数:
for(int i = 2;i<maxn;i++){
for(int j = i;j<maxn;j+=i){
que[j].push_back(i);
}
}
实现代码如下:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<string>
#include<set>
#include<vector>
#include<map>
#include<queue>
#include<deque>
using namespace std;
typedef long long LL;
const int maxn = 1000000 + 100;
int pre[maxn];
bool judge[maxn];
int ranked[maxn];
int num[maxn];
vector<int> que[maxn];
int finded(int a)
{
int v = a;
while(v!=pre[v]){
v = pre[v];
}
int j = a;
while(j!=v){
int temp = pre[j];
pre[j] = v;
j = temp;
}
return v;
}
void join(int a,int b){
int fx = finded(a);
int fy = finded(b);
if(fx!=fy){
if(ranked[fx]<ranked[fy]){
pre[fx] = fy;
}
else{
if(ranked[fx]==ranked[fy]){
ranked[fx]+=1;
}
pre[fy] = fx;
}
}
}
void init()//预处理所有的数字跟他的质因数
{
for(int i = 2;i<maxn;i++){
for(int j = i;j<maxn;j+=i){
que[j].push_back(i);
}
}
}
int main()
{
init();
int T;
scanf("%d",&T);
int t = 0;
while(T--){
t++;
int n;
memset(judge,0,sizeof(judge));
memset(ranked,0,sizeof(ranked));
scanf("%d",&n);
for(int i = 0;i<maxn;i++)pre[i] = i;
for(int i = 0;i<n;i++){////每输入一个数字,合并他和他的质因数,不能先合并!
scanf("%d",&num[i]);//因为输入了2,3而没有输入6,这里2,3应该是两个组合
for(int j = 0;j<que[num[i]].size();j++)join(num[i],que[num[i]][j]);
}
int sum = 0;
for(int i = 0;i<n;i++){
int root = finded(num[i]);
if(!judge[root]){//判断个数,组合的代表是否一样
if(num[i]!=1)//注意1,因为 1 1 1是三个组合
judge[root] = 1;
sum++;
}
}
printf("Case %d: %d\n",t,sum);
}
return 0;
}