2017多校训练第二周-Connected Components-并查集

来源:互联网 发布:学长我喜欢你 知乎 编辑:程序博客网 时间:2024/05/17 08:26

Connected Components

题目:
Driver Fang is given Nnodes, each node is labeled with an integer between 1and 1000000(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 1. Now, his task is to count the number of connected components in the graph.


题解:
题目的意思是根据两两数字之间是否有非1的最大公约数把所有数字划分成几个集合,求集合个数。
这道题目根据题面很容易想到运用并查集,至于合并集合的依据则参照GCD的结果。但是,题目卡时间,这样操作耗时太长,必须想办法优化。
对于GCD的优化,我们可以将每个数字素因子分解,并把其素因子进行集合合并,之后遍历一遍已经记录下来的拆分的所有素因子,并通过visit数组在访问过某个父亲节点后置1去重,得到最终的答案。
这里的素因子分解采用素数筛预处理出来一张素数表,在打表的过程中记录下来2~1e6之间每个数字可整除的最小素数,通过除法已经取模运算的方式拆分每个数字。
PS:一定要注意对1的处理!

代码:

#include <stdio.h>#include <iostream>#include <string.h>#include <algorithm>#define MAX 1000005using namespace std;int f[MAX] , cnt[MAX] ,temp[MAX] ,v[MAX];int mi[MAX],prime[MAX]={0},num[MAX],cc;/*long long gcd(long long a,long long b){    return b ? gcd(b,a%b):a;}*///prime[i] == 1表示i是和数,prime[i]==0表示i是素数//num[i]存储小于1e6所有素数//mi[i]存储能分解i的第一个素数void get_prime()//线性筛法得素数表{    prime[0] = 1 ;    int i , j , N = 1e6 + 2;    for(i = 2 ; i < N ; i ++)    {        if(!prime[i])        {            num[cc++] = i;            mi[i] = i ;        }        for(j=0;i*num[j]<N&&j<cc;++j){            prime[num[j]*i]=1;            mi[num[j]*i]=num[j];            if(!(i%num[j])) break;        }    }}int Find(int x){    while(f[x] != x)        x = f[x] ;    return x ;}void join(int x , int y){    int t1 = Find(x) ;    int t2 = Find(y) ;    if(t1 >= t2)        f[t1] = t2 ;    else        f[t2] = t1 ;}void init(){    for(int i = 1 ; i <= 1000000 ; i ++)        f[i] = i;}int main(){    int T ,n,k,flag,a,sum,ttt,ans;    scanf("%d" , &T) ;    cc = 0;    get_prime();    for(int cas = 1 ; cas <= T ; cas ++)    {        init() ;        memset(cnt , 0 , sizeof(cnt)) ;        memset(v,0,sizeof(v)) ;        scanf("%d" , &n) ;        k = 0 ;        sum = 0;        ans = 0 ;        for(int i = 0 ; i < n ; i ++)        {            scanf("%d" , &a) ;            if(!prime[a])            {                temp[k++] = a ;            }            else            {                while(a != 1)                {                    ttt = mi[a] ;                    temp[k++] = ttt;                    while(a % ttt == 0) a/=ttt;                    if(a == 1) break;                    //cout << ttt <<"&&&"<<a<<endl;                    join(ttt,mi[a]);                }            }        }        //cout << mi[3]<<"******"<<f[3]<<endl;        sort(temp , temp + k);        for(int i = 0 ; i < k ; i ++)        {            if(v[Find(temp[i])]==0)            {                v[Find(temp[i])] = 1;                ans ++ ;            }        }        printf("Case %d: %d\n",cas,ans);    }    return 0 ;}
1 0