[CF 1900D] Small GCD
前言
遇到数学就爆炸, 谢特
思路
不难想到转化成以下问题
求有多少三元组 \(i, j, k\), 满足 \(f(a_i, a_j, a_k) = m\), 记为 \(h(m)\)
最终输出 \(\sum m \cdot h(m)\)
你发现并不好直接计算 \(h(m)\), 但是我们发现可以通过计算 \(g(m)\) 表示 \(m | f(a_i, a_j, a_k)\) 的个数, 然后通过容斥计算 \(h(m)\)
具体的, 容斥的方法是
\[h(m) = g(m) - \sum_{\substack{j=2}}^{\lfloor V/i \rfloor} h(jm)
\]
而 \(g(m)\) 是好计算的
计算方式
for (int i = 1; i <= N; ++i) {
ll s = 0;
for (int j = i; j <= N; j += i) { // 遍历所有 i 的倍数 j
// 计算四种情况的三元组数目
f[i] += s * b[j] * (c[N] - c[j]); // 情况1:x < j < y
f[i] += s * (b[j] * (b[j] - 1) / 2); // 情况2:x 和两个 j
f[i] += b[j] * (b[j] - 1) / 2 * (c[N] - c[j]); // 情况3:两个 j 和一个 y > j
f[i] += b[j] * (b[j] - 1) * (b[j] - 2) / 6; // 情况4:三个都是 j
s += b[j]; // 更新 s:累加当前 j 的数目
}
}
其中 \(b_i\) 表示 \(i\) 的出现次数, \(c_i\) 表示小于等于 \(i\) 的出现次数
总结
常见的贡献转化方式
常见的转化, 把 \(x = y\) 的 \(y\) 的个数转化为计算 \(x | y\) 的 \(y\) 的个数
这样子本质上可以把 \(\rm{gcd}\) 的对指数取 \(\min\) 转化成只要指数大于目标值即可, 方便计算

浙公网安备 33010602011771号