算法
(数论) $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$。
C++ 代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 45000, M = 50;
int primes[N], cnt;
bool st[N];
PII factor[M];
int cntf;
int divider[N], cntd;
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
void dfs(int u, int p)
{
if (u > cntf)
{
divider[cntd ++ ] = p;
return;
}
for (int i = 0; i <= factor[u].second; i ++ )
{
dfs(u + 1, p);
p *= factor[u].first;
}
}
int main()
{
get_primes(N);
int n;
scanf("%d", &n);
while (n -- )
{
int a0, a1, b0, b1;
scanf("%d%d%d%d", &a0, &a1, &b0, &b1);
int d = b1;
cntf = 0;
for (int i = 0; primes[i] <= d / primes[i]; i ++ )
{
int p = primes[i];
if (d % p == 0)
{
int s = 0;
while (d % p == 0) s ++, d /= p;
factor[ ++ cntf] = {p, s};
}
}
if (d > 1) factor[ ++ cntf] = {d, 1};
cntd = 0;
dfs(1, 1);
int res = 0;
for (int i = 0; i < cntd; i ++ )
{
int x = divider[i];
if (gcd(x, a0) == a1 && (LL)x * b0 / gcd(x, b0) == b1)
{
res ++ ;
}
}
printf("%d\n", res);
}
return 0;
}
为什么N=45000呢?
用试除法分解质因数只需要枚举到 $\sqrt {2 \times 10^9}$,所以 $N$ 只要取到比这个数大即可。
y总, DFS枚举出 b1 的所有约数的时间大概是多少啊
int范围内的数约数个数最多有1600多个。
为什么M=50呀
M表示 $b_1$ 中不同质因子的个数,这里开得比较宽松,开小一点也是可以的。
哦哦
## 资瓷
谢谢hh
闫神你好,引言中,$1\sim N$的质数个数为$\frac{N}{lnN}$,应给是这个。
笔误了,已改hh
能不能有二分枚举出来符合条件的左边结合右边界,在 a1《 x 《 b1 区间中
为什么while循环没记录时间复杂度
tql
dfs看不懂
终于看懂了,建议画出递归搜索过程有助于理解递归步骤
dfs写的啥意思呀?看不懂
求讲解
https://www.acwing.com/activity/content/code/content/5876715/ 你看下
懂了,谢谢
这个dfs有点全排列的意思
就像二进制枚举一样,任何一个组合都是它的约数
资瓷
这个搜索我看不懂哇QAQ,有无大佬教教我 球球
我的理解是,for(int i = 0 ; i < second ; i ++),第一次循环就是等于乘以1了,而u则是*多少种类的质因子,如果超过了则return , 这样的话就会将所有的可能因子求出
呜呜第一次循环就相当于a1^0次方咯
y总,你的数组刚好开到N,传了N,欧拉筛遍历又遍历到N,所以判断st[N]的时候会出现数组越界的问题!!!就会出现玄学错误!!!我自己敲的时候就出现了玄学错误,找了一晚上的bug!!!吐了呀!!!
take pity on you 这也是一个很好的经验呢,不是吗? segment fault最为玄幻
怎么解决的
分解质因数那里的时间复杂度 那里是不是写错了,应该是 $\sqrt{b_i}/{\log{\sqrt{b_i}}}$
$ O(\log \sqrt b _ i) = O(\log b _ i) $
yls请问那个“平均每个数的约数个数为 logn 个”也是一个定理吗qwq?
“时间复杂度分析”那里“每组测试数据分解 b1的计算量”应该没有 n 吧
应该说的 是总的计算量
想请教一下,dfs求约数这一步的时间复杂度怎么算呀
应该是约数最多就1600个,dfs爆搜也忽略不计的
%%%