范围内质数倍数集合合并——洛谷P1621
https://www.luogu.com.cn/problem/P1621 洛谷题目
其实,一开始思路想写数学的容斥,后来发现其实很难写。排除起来很麻烦。虽然省了真正的合并,但是,约数之间的计算更加繁琐。
所以,偶然想到了素筛法,能够提高对应效率。
尝试思路:1、素筛所有素数;2、将所有素数保存;3、从素数数组中找到第一个>=起点值的质数位置,从此往后尝试合并;4、继续用素筛方法合并:重点是找到第一个>=当前枚举素数的值,然后进行合并。
本题使用算法设计到了三个:素数筛法、二分查找(可以用也可以不用)、素筛和并查集一起使用的合并。
完成代码:
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 7 int f[100005]; 8 int z[100000]; 9 bool s[100005]; 10 11 int getf(int x) 12 { 13 if (f[x] == x) 14 return x; 15 return f[x] = getf(f[x]); 16 } 17 18 int main(int argc, char** argv) 19 { 20 int a, b; 21 int q; 22 int i, j; 23 int n, m; 24 int fx, fy; 25 int w;//数组起始位置 26 cin >> a >> b >> q; 27 n = b - (a - 1); 28 memset(s, 1, sizeof(s)); 29 s[0] = s[1] = m = 0; 30 for (i = 2; i * i <= 100000; i++) 31 { 32 if (s[i]) 33 { 34 z[m++] = i; 35 for (j = i * i; j <= 100000; j += i) 36 { 37 s[j] = 0; 38 } 39 } 40 } 41 for (; i <= 100000; i++) 42 if (s[i]) 43 z[m++] = i; 44 for (i = 0; i <= b; i++) 45 f[i] = i; 46 /* 47 cout << m << endl; 48 for (i = 0; i < m; i++) 49 cout << z[i] << " "; 50 cout << endl; 51 */ 52 //cout << n << endl; 53 w = lower_bound(z, z + m, q) - z; 54 //cout << z[w] << endl; 55 i = w; 56 while (i < m && z[i] <= b) 57 { 58 int t = a / z[i]; 59 if (a % z[i] != 0) 60 t++; 61 t = t * z[i]; 62 //cout << t << endl; 63 int f1 = getf(t), f2; 64 for (j = t + z[i]; j <= b; j += z[i]) 65 { 66 f2 = getf(j); 67 //cout << t << " " << j << ":" << f1 << " " << f2 << endl; 68 if (f1 != f2) 69 { 70 if (f1 > f2) 71 swap(f1, f2); 72 f[f2] = f1; 73 n--; 74 } 75 } 76 i++; 77 } 78 cout << n << endl; 79 return 0; 80 }