Luckyleaves's Blog

stay hungry,stay foolish greedy and lucky

P1072 Hankson的趣味题(解题概要)

题目传送门

(数论) $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)//枚举可能的 x
{
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 ++)//枚举出可行的 X的因数
{
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;
}