题解:洛谷 P10263 公倍数问题

【题目来源】

洛谷:P10263 [GESP202403 八级] 公倍数问题 - 洛谷

【题目描述】

小 A 写了一个 \(N \times M\) 的矩阵 \(A\),我们看不到这个矩阵,但我们可以知道,其中第 \(i\) 行第 \(j\) 列的元素 \(A_{i,j}\)\(i\)\(j\) 的公倍数(\(i=1,\dots,N\)\(j=1,\dots,M\))。现在有 \(K\) 个小朋友,其中第 \(k\) 个小朋友想知道,矩阵 \(A\) 中最多有多少个元素可以是 \(k\)\(k=1,2,\dots,K\))。请你帮助这些小朋友求解。

注意:每位小朋友的答案互不相关,例如,有些位置既可能是 \(x\),又可能是 \(y\),则它同时可以满足 \(x,y\) 两名小朋友的要求。

方便起见,你只需要输出 \(\sum_{k=1}^{K}{k \times \texttt{ans}_k}\) 即可,其中 \(\texttt{ans}_k\) 表示第 \(k\) 名小朋友感兴趣的答案。

【输入】

第一行三个正整数 \(N,M,K\)

【输出】

输出一行,即 \(\sum_{k=1}^{K}{k \times \texttt{ans}_k}\)

请注意,这个数可能很大,使用 C++ 语言的选手请酌情使用 long long 等数据类型存储答案。

【输入样例】

2 5 2

【输出样例】

9

【核心思想】

  1. 问题分析:给定 \(N \times M\) 矩阵,\(A_{i,j}\)\(i\)\(j\) 的公倍数。对每个 \(k \in [1, K]\),求矩阵中最多有多少个元素可以是 \(k\),即满足 \(k\)\(i\)\(j\) 的公倍数的位置 \((i,j)\) 的数量。最终输出 \(\sum_{k=1}^{K} k \times ans_k\)。这是一个因数计数 + 乘法原理问题,核心在于将"公倍数"条件转化为"因数"计数。

  2. 算法选择

    • 因数个数预处理\(a[i]\) 表示 \([1, N]\)\(i\) 的因数个数,\(b[i]\) 表示 \([1, M]\)\(i\) 的因数个数
    • 乘法原理\(ans_k = a[k] \times b[k]\),即 \(i\)\(k\) 的因数且 \(j\)\(k\) 的因数的 \((i,j)\) 对数
  3. 关键步骤

    • 读入数据:读取 \(N, M, K\)
    • 预处理因数个数\(count\_divisor(n, divisor[])\)):
      • 枚举 \(i\)\(1\)\(n\)
        • 枚举 \(j\)\(i\)\(MAX\) 步长 \(i\)
          • \(divisor[j] \leftarrow divisor[j] + 1\)\(i\)\(j\) 的因数)
    • 计算答案\(i\)\(1\)\(K\)):
      • \(ans \leftarrow ans + i \times a[i] \times b[i]\)
    • 输出结果\(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(MAX \log MAX + K)\),其中 \(MAX = 10^6\),因数预处理近似 \(O(MAX \log MAX)\)
    • 空间复杂度:\(O(MAX)\),因数计数数组
  5. 因数计数与乘法原理的核心思想

    • 公倍数的因数分解\(k\)\(i\)\(j\) 的公倍数 \(\Leftrightarrow i | k\)\(j | k\)。因此位置 \((i,j)\) 可以是 \(k\) 当且仅当 \(i\)\(j\) 都是 \(k\) 的因数
    • 乘法原理应用\([1, N]\)\(k\) 的因数有 \(a[k]\) 个,\([1, M]\)\(k\) 的因数有 \(b[k]\) 个,每个因数对 \((i,j)\) 对应一个矩阵位置,因此 \(ans_k = a[k] \times b[k]\)
    • 埃氏筛思想预处理:通过枚举因数 \(i\) 并标记其倍数,高效计算每个数的因数个数,时间复杂度 \(O(MAX \log MAX)\),远优于逐个数的 \(O(\sqrt{n})\) 分解
    • 答案独立性:题目说明每位小朋友的答案互不相关,因此可以直接累加各 \(k\) 的贡献,无需考虑元素冲突
    • 适用于因数统计、乘法原理组合计数类问题

【算法标签】

普及 #约数

【代码详解】

#include <bits/stdc++.h>
using namespace std;

#define int long long  // 定义int为long long类型
int n, m, k, ans;      // n: 行数, m: 列数, k: 遍历范围, ans: 答案
int a[1000005], b[1000005];  // a: 存储1~1000000的因数个数(针对n), b: 存储1~1000000的因数个数(针对m)

// 预处理函数:计算1~1000000的因数个数
// divisor[]: 存储结果的数组
// n: 实际需要计算的最大值
void count_divisor(int n, int divisor[])
{
    // 类似埃氏筛法计算每个数的因数个数
    for (int i = 1; i <= n; i++)  // i是可能的因数
    {
        // 将i的倍数都增加1,因为i是这些数的因数
        for (int j = i; j <= 1000000; j += i)
        {
            divisor[j] += 1;  // j的因数个数加1
        }
    }
}

signed main()  // 因为#define int long long,所以用signed main
{
    // 输入n, m, k
    cin >> n >> m >> k;

    // 预处理计算因数个数
    // a[i]: 表示在1~n范围内,i的因数个数
    // b[i]: 表示在1~m范围内,i的因数个数
    count_divisor(n, a);
    count_divisor(m, b);

    // 计算答案
    for (int i = 1; i <= k; i++)  // 遍历1到k
    {
        // 计算公式:ans = Σ(i=1 to k) [i * a[i] * b[i]]
        ans += i * a[i] * b[i];

        // 调试输出
        // cout << "ans " << ans << endl;
    }

    // 输出结果
    cout << ans << endl;

    return 0;
}

【运行结果】

2 5 2
9
posted @ 2026-07-17 16:02  团爸讲算法  阅读(3)  评论(0)    收藏  举报