积性函数线性筛

· · 算法·理论

我忽然想起来可以写一个积性函数线性筛。所以我写了一个。

template<typename T, typename F>
vec<T> multiplicative_function_table(u32 n, F &&g)
{
    vec<u32> prime;
    vec<bool> vis(n + 1);
    prime.reserve(n / 15);
    vec<T> res(n + 1);
    res[1] = T(1);
    for (u32 i = 2, t, k, ni; i <= n; i++) {
        if (!vis[i]) {
            prime.emplace_back(i);
            res[i] = g(i, 1);
        }
        for (u32 j: prime) {
            if (i * j > n) break;
            vis[i * j] = true;
            t = i / j;
            if (i == t * j) {
                k = 1;
                do ni = t, t /= j, k++;
                while (ni == t * j);
                res[i * j] = res[ni] * g(j, k);
                break;
            }
            res[i * j] = res[i] * g(j, 1);
        }
    }
    return res;
}

传入一个函数(或者仿函数) gg(x, y) 返回 f(x^y) 即可(保证 x 是质数)。

这段代码有个比较直觉的地方是,当 i % j == 0 (也就是 i == t * j)的时候,我直接暴力将 i 分解为了 j^k \times x。感觉这个复杂度就很对,总复杂度是线性的。

经过打表,我认为这玩意就是对的。我打出的表显示,1nk 之和(也就是,每个数的最小质因子个数之和)约等于 1.612nn10 开始一直到 10^8,比值都保持在 1.612

这个性质的意义是,线性筛积性函数的限制更松了:若求 f(p^k) 的复杂度是 O(k),那么筛出 [1, n]f 值也是 O(n) 的。

打表程序。

相关讨论。

@syp11 大佬指出其实不止 O(k)f(p^k) 是线性的,只要 O(\text{poly}(k)) 就可以,只不过常数更大。比如 k^2 的常数是 3.9321, k^3 常数是 14.9878

upd: 坏了其实只有 O(k) 算是可以的。若 O(k^x),只要 x \leq 1 比值就是收敛的,否则发散。原因:syp11 的文章。