原题题面:Hankson的趣味题
思路:
在没想到纯数学逻辑前,容易想到一个朴素算法:用试除法求出 b1 的所有约数,之后逐一检验是否满足条件,该暴搜的时间复杂度大概是O(n*
b
1
\sqrt b1
b1 * log b1)。实际测试会有一个测试数据过不了。
那么就是优化问题了。
由唯一分解定理可知:如果 x 是 b1 的约数,则x所有的质因数都是b1的质因数。
由此我们可以先预先处理出 1 ~
b
1
\sqrt b1
b1 的所有质数,然后用这些质数去试除 b1,得到相应的质因子及其数量。最后用DFS枚举出每一个b1的约数,并将其存储到数组中,再进行判断条件。
质数定理:1 ~ n 中的质数个数约为
n
/
l
n
(
n
)
n/ln(n)
n/ln(n)
由质数定理可知,可以在
(
b
i
)
\sqrt (b~i~)
(b i ) / log(bi) 时间复杂度内将 b1 的质因数都分解出来。所以总的时间复杂度为O(n*
(
b
1
)
\sqrt(b1)
(b1) / log(b1))。这样就可以过了。
纯数学逻辑来做,我们可以推出当 x 与 a0 互质时
gcd(
x
/
a
1
x/a1
x/a1,
a
0
/
a
1
a0/a1
a0/a1) ==1
gcd(
b
1
/
x
b1/x
b1/x,
b
0
/
b
1
b0/b1
b0/b1) ==1
所以我们就可以直接枚举1~
(
b
1
)
\sqrt (b1)
(b1) 内的所有b1的约数x,然后将其拉去判断上面两个条件就行了。
注意:这样会少统计了大于
(
b
1
)
\sqrt(b1)
(b1)的约数,所以应写一个data存储 b1/x,也将data拿去判断。
时间复杂度约为O(n *
(
b
1
)
\sqrt(b1)
(b1))。
代码如下:
- 暴搜
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
//预先算一下sqrt(2e9),易知其小于45000
//易知,2e9中不可能有多余10个不同的质因子,开50宽松一点。
const int N = 45000, M = 50;
int n;
int primes[N], cnt;
bool st[N];
PII factor[M];
int cnt_f;
int divider[N], cnt_d;
void get_primes(int x)
{
for(int i = 2; i <= x; i++)
{
if(!st[i]) primes[cnt ++] = i;
for(int j = 0; primes[j] <= x / i; i++)
{
st[i * primes[j]] = 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 > cnt_f)
{
divider[cnt_d ++] = p;
return ;
}
for(int i = 0; i <= factor[u].second; i++)
{
dfs(u + 1, p);
p *= factor[u].first;
}
}
int main()
{
get_primes(N);
cin >> n;
while(n--)
{
int a0, a1, b0, b1;
cin >> a0 >> a1 >> b0 >> b1;
int t = b1;
cnt_f = 0;
for(int i = 0; i < cnt && primes[i] <= t / primes[i]; i++)
{
if(t % primes[i] == 0)
{
int s = 0;
while(t % primes[i] == 0)
{
t /= primes[i];
s ++;
}
factor[++ cnt_f] = {primes[i], s};
}
}
if(t > 1) factor[++ cnt_f] = {t, 1};
cnt_d = 0;
dfs(1, 1);
int ans = 0;
for(int i = 0; i < cnt_d; i++)
{
int x = divider[i];
if(gcd(x, a0) == a1 && (LL)x * b0 / gcd(x, b0) == b1)
ans ++;
}
cout << ans << endl;
}
return 0;
}
- 数学思路
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int main()
{
int n;
cin >> n;
while(n--)
{
int a0, a1, b0, b1;
cin >> a0 >> a1 >> b0 >> b1;
int ans = 0;
for(int x = 1; x <= b1 / x; x++)
{
if(b1 % x == 0)
{
if(x % a1 == 0 && gcd(x / a1, a0 / a1) == 1 && gcd(b1 / b0, b1 / x) == 1) ans ++;
int y = b1 / x;
if(x == y) continue;
if(y % a1 == 0 && gcd(y / a1, a0 / a1) == 1 && gcd(b1 / b0, b1 / y) == 1) ans ++;
}
}
cout << ans << endl;
}
return 0;
}
非常感谢您能看到这里~~~有问题可以私信我哈。
博客探讨了Hankson的趣味题的解题思路,从最初的暴力搜索算法(时间复杂度O(n*b1*log b1))到利用质因数分解优化为O(n*b1/log b1),再到最终通过数学逻辑简化为O(n*b1)。文章介绍了如何利用唯一分解定理和质数定理来优化解题过程。
&spm=1001.2101.3001.5002&articleId=107252019&d=1&t=3&u=6049e01beae844f9a97da2b2034af2f0)
269

被折叠的 条评论
为什么被折叠?



