2017多校训练第二周-Connected Components-并查集
2017-03-12 23:14
288 查看
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 1000005 using 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 ; }
相关文章推荐
- 算法训练 - 并查集
- 2017/7/31训练日记(并查集基础)
- CSU-ACM2017暑期训练10-并查集&&HASH C - 食物链 POJ - 1182 (并查集好题)
- 《算法指南-训练指南》第三章-3.5_LA 3644(并查集)
- 【2017多校训练08 1002】【HDOJ 6134】Battlestation Operational
- 2017多校训练Contest4: 1005 Lazy Running hdu6071
- 《算法竞赛-训练指南》第三章-3.6_LA 3027(并查集)
- (2017多校训练第一场)HDU - 6038 Function 排列分解
- 2017多校训练第10场-Schedule(贪心+二分)
- 训练第二周之DFS(深度优先搜索)
- 2017多校训练Contest5: 1006 Rikka with Graph hdu6090
- 省赛训练之并查集(五)
- 2017多校训练第二场 hdu6045 Is Derek lying
- 2017多校训练-A Water Problem-DP
- 2017多校训练Contest2: 1009 TrickGCD hdu6053
- 寒假训练--并查集--电影节
- 算法训练 安慰奶牛 (并查集—路径压缩、Kruskal算法)
- 暑假训练专题二 并查集的理解 优先队列
- 2017多校训练第一场
- 2017多校训练八-1011(hdu 6143 Killer Names)