k 能放在 (i,j) 位置,当且仅当 i 和 j 都是 k 的约数。
P10263 [GESP202403 八级] 公倍数问题
GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。
一、先回答问题
对每个 k 单独试除找约数,会把 K=1e6 的数据拖慢。
二、核心观察
c1[k] 统计 [1,N] 中 k 的约数个数,c2[k] 统计 [1,M] 中 k 的约数个数。
为什么不丢答案:枚举约数 i,把贡献加给所有倍数 k,正好覆盖所有 i|k 的关系。
约数 i
倍数 k
c1/c2
三、互动演示
步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推
点击下一步开始。
四、自己选答案
为什么 ans_k=c1[k]*c2[k]?
先判断,再点按钮。
复杂度为什么可过?
先判断,再点按钮。
五、手推结果
| 对象 | 维护值 | 意义 |
|---|---|---|
| 行 | 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;
}