[NOIP2001-普及]最大公约数和最小公倍数问题

GitHub跳转原题关系图返回列表

x0∤y0 直接判 0;否则令 k=y0/x0,问题化为把 k 拆成互质有序对,每个质因子幂整段分配,答案 2^ω(k)。

OJ: roj

题目 ID: 3505

难度:普及-

标签:数论最大公约数质因数分解计数python

创建: 2026-10-02 04:16

更新: 2026-10-06 12:16

题目描述

给定正整数 x0,y0x_0,y_0,求有序正整数对 (P,Q)(P,Q) 的个数,满足 gcd⁡(P,Q)=x0\gcd(P,Q)=x_0 且 lcm⁡(P,Q)=y0\operatorname{lcm}(P,Q)=y_0。

输入一行两个正整数 x0,y0x_0,y_0;输出一行一个整数表示答案。

样例:3 603\ 60 时答案为 44。

思路

若 x0∤y0x_0 \nmid y_0 则答案为 00。否则令 k=y0/x0k=y_0/x_0,问题等价于把 kk 拆成互质有序对 ab=kab=k,每个质因子幂只能整段给 aa 或 bb,方案数为 2ω(k)2^{\omega(k)}。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-02 04:16
# update_at: 2026-10-02 04:16

import sys


def distinct_primes(k: int) -> int:
    """k 的不同质因数个数 ω(k):试除到 √k,除尽的因子只计一次。"""
    count = 0
    d = 2
    while d * d <= k:
        if k % d == 0:
            count += 1
            while k % d == 0:  # 把 d 的整段幂次除尽,剩下的部分不再含 d
                k //= d
        d += 1
    return count + (k > 1)  # 剩余的 k 是一个大质数


def solve() -> None:
    x0, y0 = map(int, sys.stdin.buffer.read().split())

    # gcd(P,Q)=x0 且 lcm(P,Q)=y0 有解 ⇔ x0 | y0(gcd 必整除 lcm),否则答案为 0。
    if y0 % x0 != 0:
        print(0)
        return

    # 令 P=x0*a, Q=x0*b:gcd(a,b)=1 且 a*b=k(k=y0/x0)。
    # 互质要求把 k 的每个质因子整段幂次 p^e 全给 a 或全给 b,共 2^ω(k) 个有序对。
    k = y0 // x0
    print(1 << distinct_primes(k))


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-06 12:16
 * update_at: 2026-10-06 12:16
 */
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

ll x0_val, y0_val; // 题目输入:gcd 目标与 lcm 目标(避免 y0 与 math.h 的贝塞尔函数冲突)

// 返回 k 的不同质因子个数 ω(k)
int distinct_primes(ll k) {
    int cnt = 0;
    for (ll d = 2; d * d <= k; ++d) {
        if (k % d == 0) {
            ++cnt;          // 发现一个质因子,只计一次
            while (k % d == 0) k /= d; // 把 d 的整段幂除尽
        }
    }
    return cnt + (k > 1);   // 剩余部分若大于 1 是一个大质因子
}

int main() {
    scanf("%lld %lld", &x0_val, &y0_val);

    // gcd 必须整除 lcm,否则不存在这样的正整数对
    if (y0_val % x0_val != 0) {
        printf("0\n");
        return 0;
    }

    ll k = y0_val / x0_val;
    // 令 P=x0*a, Q=x0*b,则 ab=k 且 gcd(a,b)=1。
    // 互质要求 k 的每个质因子幂整段给 a 或 b,共 2^ω(k) 个有序对。
    printf("%d\n", 1 << distinct_primes(k));
    return 0;
}