题目传送门
(数论) $O(n \sqrt{b_1} / log(b_1))$
由于 $[x, b_0] = b_1$,因此 $x$ 一定是 $b_1$ 的约数。
所以我们可以枚举 $b_1$ 的所有约数,然后依次判断是否满足 $[x, b_0] = b_1$ 以及 $(x, a_0) = a_1$ 即可。
如果直接用试除法求 $b_1$ 的所有约数,那么总计算量是 $n \sqrt{b_1} = 2000 * \sqrt{2 \times 10^9} \approx 10^8$,会有一个测试数据超时。
我们可以先预处理出 $1 \sim \sqrt{b_1}$ 内的所有质数,然后用这些质数去试除 $b_1$。
由质数定理:
- $1 \sim n$ 中的质数个数约为 $\frac{n}{ln(n)}$。
因此我们可以在 $\sqrt{b_i} / log(b_i)$ 的时间复杂度内将 $b_1$ 分解质因数。然后通过DFS枚举出 $b_1$ 的所有约数。
时间复杂度分析:
- 一共 $n$ 组测试数据,每组测试数据分解 $b_1$ 的计算量是 $n \sqrt{b_1} / log(b_1) \approx 10^7$。
平均每个数的约数个数为 $logn$ 个,计算最小公倍数和最大公约数的时间复杂度也是 $O(logn)$,因此判断 $x$ 是否合法的计算量是 $nlog^2n \approx 2 \times 10^6$。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| #include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N = 45005,M = 55; int pr[N], cnt; int n; bool bl[N]; pair<int, int>q[M]; int cnta; int di[N], cntd; void inti(int x) { for (int i = 2; i <= x; i ++) { if (!bl[i]) pr[cnt ++] = i; for (int j = 0; pr[j] <= x/i; j ++) { bl[pr[j] * i] = true; if (!(i % pr[j])) break; } } } int gcd(int a, int b) { return b ? gcd(b, a%b) : a; } void dfs(int l, int p) { if(l > cnta) { di[cntd ++] = p; return ; } for(int i = 0;i <= q[l].second;i ++) { dfs(l + 1,p); p *= q[l].first; } } int main() { inti(N); scanf("%d", &n); while(n --) { int a0, a1, b0, b1; scanf("%d%d%d%d", &a0, &a1, &b0, &b1); int d = b1; cnta = 0; for (int i = 0; pr[i] <= d/pr[i]; i ++) { int p = pr[i]; if(!(d%p)) { int s = 0; while(d%p == 0) s ++, d /= p; q[++ cnta] = {p, s}; } } if(d > 1) q[++ cnta] = {d, 1}; cntd = 0; dfs(1, 1); int res = 0; for(int i = 0; i < cntd; i ++) { int x = di[i]; if(gcd(x, a0) == a1 &&(long long)x*b0/gcd(x, b0) == b1) { res ++; } } printf("%d\n", res); } return 0; }
|