神秘技巧:O(n lnln n) 的dirichlet卷积
要求其中一个是积性函数,另一个可以任意。
其实就是在质因数分解的意义下做高维前缀和。
高维前缀和
对于二维前缀和,我们也许可以直接推 s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j]
而我们也可以
s[i][j]=s[i][j-1]+a[i][j]
然后
s[i][j]=s[i-1][j]+a[i][j]
以实现二维的前缀和。
换句话说我们可以先枚举维,然后对每一维做前缀和。
这里咋做
我们设 \(f\) 是那个积性函数,\(g\) 是任意的。
首先我们枚举“维”。维是啥?自然就是每个质数
注意到一个积性函数在 \(p^1,p^2...p^k\) 处的值是不确定的。我们需要枚举它,相当于钦点 \(p\) 的次数。然后做一个如下操作:
\(g(i)\times f(p^k)\to g(i\times p^k)\)。
注意到这里钦点 \(p^k\) 是指恰好 \(f\) 那里的 \(p\) 就是 \(k\) 次。所以我们需要倒着枚举 \(i\),类似背包的考虑,否则会导致重复贡献。
然后就枚举质数 \(p\),倒着枚举 \(i\),枚举 \(p^k\),做一个前缀和就行了。
这样的复杂度据说是 \(O(n\log\log n)\) 的。实际跑出来也比 \(O(n\ln n)\) 快很多。
特殊情况
如果 \(f\) 是完全积性函数,那你只需要这样做:
for(p: primes)
{
for(int i=1;i<=n/p;++i)
{
g[i*p]+=g[i]*f[p];
}
}
同样是 \(O(n\log\log n)\) ,常数略小些。