Loading

[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\) 转化成只要指数大于目标值即可, 方便计算

posted @ 2025-03-25 11:00  Yorg  阅读(21)  评论(0)    收藏  举报