欧拉筛 分解质因数 枚举约数 最大公因数与最小公倍数 DFS T4
P1072 [NOIP2009 提高组] Hankson 的趣味题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

Hanks 博士是 BT(Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫 Hankson。现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题。

今天在课堂上,老师讲解了如何求两个正整数 的最大公约数和最小公倍数。现在 Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数 ,设某未知正整数 满足:

  1. 的最大公约数是

  2. 的最小公倍数是

Hankson 的“逆问题”就是求出满足条件的正整数 。但稍加思索之后,他发现这样的 并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的 的个数。请你帮助他编程求解这个问题。

输入格式

第一行为一个正整数 ,表示有 组输入数据。接下来的 行每行一组输入数据,为四个正整数 ,每两个整数之间用一个空格隔开。输入数据保证 能被 整除, 能被 整除。

输出格式

行。每组输入数据的输出结果占一行,为一个整数。

对于每组数据:若不存在这样的 ,请输出 ,若存在这样的 ,请输出满足条件的 的个数;

样例 #1

样例输入 #1

2 
41 1 96 288 
95 1 37 1776

样例输出 #1

6 
2

提示

【样例解释】

第一组输入数据, 可以是 ,共有 个。

第二组输入数据, 可以是 ,共有 个。

【数据范围】

  • 对于 的数据,保证有
  • 对于 的数据,保证有

NOIP 2009 提高组 第二题

思路

求满足 , 个数。

根据 , 则 一定是 的约数, 可以直接枚举所有约数然后判断是否成立。且 2e9 的范围内的约数个数最多也只有 1536 个约数。

此时 可以直接判断, 可以通过 来判断。 这两步的时间复杂度为

因此对于每组数据可以用 大概是 大概也就 1e7 来判断, 够用。

但是在枚举 的所有约数时, 按照之前的枚举思路, 最坏就是 的复杂度。

已经足够好了, 这里再加个小优化: 枚举时不要枚举所有数, 而是先将 分解成质因数, 直接枚举所有质数来构成其分解质因式。

因为 , 其质数个数有 个, 再用DFS爆搜出所有的约数, INT范围内总共最大也就1600, 故基本忽略不记。

这种优化就可以快十倍, 让原本 的复杂度变成 , 基本就可以过了。

代码

#include <bits/stdc++.h>
using namespace std;
 
typedef long long LL;
const int N = 1e6 + 10;
int primes[N], cnt;
bool st[N];
 
struct Factor
{
    int p, s;
} factor[1601];
int fcnt;
 
int dividor[N], dcnt;
 
void init()
{
    for (int i = 2; i < N; i++)
    {
        if (!st[i])
            primes[cnt++] = i;
        for (int j = 0; primes[j] * i < N; j++)
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0)
                break;
        }
    }
}
 
void dfs(int u, int p)
{
    if (u == fcnt)
    {
        dividor[dcnt++] = p;
        return;
    }
 
    for (int i = 0; i <= factor[u].s; i++)
    {
        dfs(u + 1, p);
        p = factor[u].p * p;
    }
}
 
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}
 
void solve()
{
    int a, b, c, d;
    cin >> a >> b >> c >> d;
 
    fcnt = 0;
    int t = d;
    for (int i = 0; primes[i] <= t / primes[i]; i++)
    {
        int p = primes[i];
        if (t % p == 0)
        {
            int c = 0;
            while (t % p == 0)
                t /= p, c++;
            factor[fcnt++] = {p, c};
        }
    }
    if (t > 1)
        factor[fcnt++] = {t, 1};
 
    // DFS搜约数
    dcnt = 0;
    dfs(0, 1);
 
    int res = 0;
    for (int i = 0; i < dcnt; i++)
    {
        int x = dividor[i];
        if (gcd(x, a) == b && (LL)x * c / gcd(x, c) == d)
            res++;
    }
    cout << res << endl;
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    init();
    int T;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}