P10263 [GESP202403 八级] 公倍数问题

GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。

一、先回答问题

结论

k 能放在 (i,j) 位置,当且仅当 i 和 j 都是 k 的约数。

容易误入的路

对每个 k 单独试除找约数,会把 K=1e6 的数据拖慢。

二、核心观察

建模与压缩

c1[k] 统计 [1,N] 中 k 的约数个数,c2[k] 统计 [1,M] 中 k 的约数个数。

为什么不丢答案:枚举约数 i,把贡献加给所有倍数 k,正好覆盖所有 i|k 的关系。

约数 i 倍数 k c1/c2

三、互动演示

步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推

四、自己选答案

问题 1

为什么 ans_k=c1[k]*c2[k]?

问题 2

复杂度为什么可过?

五、手推结果

对象维护值意义
c1[k]N 范围内约数数
c2[k]M 范围内约数数
答案k*c1*c2加权贡献

课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。

六、C++14 参考代码

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

const int MAXK = 1000005;
int c1[MAXK], c2[MAXK];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m, k;
    cin >> n >> m >> k;

    // 统计每个 k 在 [1, N] 范围内的约数个数
    for (int i = 1; i <= n && i <= k; i++) {
        for (int x = i; x <= k; x += i) {
            c1[x]++;
        }
    }

    // 统计每个 k 在 [1, M] 范围内的约数个数
    for (int j = 1; j <= m && j <= k; j++) {
        for (int x = j; x <= k; x += j) {
            c2[x]++;
        }
    }

    long long total_sum = 0;
    for (int x = 1; x <= k; x++) {
        // ans_x = c1[x] * c2[x]
        total_sum += (long long)x * c1[x] * c2[x];
    }

    cout << total_sum << endl;

    return 0;
}