NOIP2009提高组.Hankson的趣味题
目录
- 题目
- 算法标签: 数论, 最大公约数, 最小公倍数, 约数
- 思路
- 代码
题目
200. Hankson的趣味题
算法标签: 数论, 最大公约数, 最小公倍数, 约数
思路
因为 [ x , a 0 ] = b 1 [x, a_0] = b_1 [x,a0]=b1因此 x x x一定是 b 1 b_1 b1约数, 注意到, 数据范围是 2 × 1 0 9 2 \times 10 ^ 9 2×109如果直接使用试除法计算约数时间复杂度是 O ( n n ) O(n \sqrt n) O(nn)会超时, 因此需要进行优化,
可以将 1 ∼ n 1 \sim \sqrt n 1∼n之间的质数全部预处理出来, 然后将 b 1 b_1 b1转化为算数基本定理形式 因为在 i n t int int范围内约数个数最多的数的约数个数大约是 1600 1600 1600个, 因此有效降低了时间复杂度
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>using namespace std;typedef long long LL;
typedef pair<int, int> PII;
//预处理的质数的个数和约数个数
const int N = 50000, M = 100;int primes[N], cnt;
bool vis[N];
PII factor[M];
int f_cnt;
int divs[N], d_cnt;void get_primes() {for (int i = 2; i < N; ++i) {if (!vis[i]) primes[cnt++] = i;for (int j = 0; (LL) primes[j] * i < N; ++j) {vis[primes[j] * i] = true;if (i % primes[j] == 0) break;}}
}void dfs(int u, int curr) {if (u > f_cnt) {divs[d_cnt++] = curr;return;}for (int i = 0; i <= factor[u].second; ++i) {dfs(u + 1, curr);curr *= factor[u].first;}
}int gcd(int a, int b) {return b ? gcd(b, a % b) : a;
}int main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);get_primes();int n;cin >> n;while (n--) {// (x, a0) = a1, [x, b0] = b1int a0, a1, b0, b1;cin >> a0 >> a1 >> b0 >> b1;// 分解质因数int val = b1;f_cnt = 0;for (int i = 0; primes[i] <= val / primes[i]; ++i) {if (val % primes[i] == 0) {int c = 0;while (val % primes[i] == 0) val /= primes[i], c++;factor[++f_cnt] = {primes[i], c};}}if (val > 1) factor[++f_cnt] = {val, 1};// 求b1的所有约数d_cnt = 0;dfs(1, 1);int ans = 0;//枚举所有约数, 判断是否符合条件for (int i = 0; i < d_cnt; ++i) {int x = divs[i];if (gcd(x, a0) == a1 && ((LL) x * b0 / gcd(x, b0)) == b1) ans++;}cout << ans << "\n";}return 0;
}