关于 gcd 卷积

· · 算法·理论

下面我们默认只考虑 1 \sim N 之内的正整数。这样子求和下标写起来可以更加简洁一些。

考虑这么一个问题:给定两个数论函数和一个正整数 n,求 h(n)=\sum\limits_{\gcd(i,j)=n}f(i)g(j)

首先有一个东西叫做 Zeta 变换:f 的 Zeta 变换满足 \hat{f}(n)=\sum\limits_{n \mid k}f(k)。 然后我们注意到一个事实:

\hat{h}(n)=\sum\limits_{n \mid k}h(k) \\ =\sum\limits_{n \mid k}\sum\limits_{\gcd(i,j)=k}f(i)g(j)

此时,若 n \mid k\gcd(i,j)=k 同时成立,手模一下可知 n \mid in \mid j。于是,

\hat{h}(n)=\sum\limits_{n \mid i,n \mid j}f(i)g(j) \\ =(\sum\limits_{n \mid i}f(i)) \times (\sum\limits_{n \mid i}g(i)) \\ =\hat{f}(n) \times \hat{g}(n)

欸,这个时候我们发现这个东西和我们所熟悉的卷积,和 FFT 和 NTT 啥的很像啊。我们可以把求 \hat{f},\hat{g} 视为一次变换,然后算出点积,最后试着逆变换回去。

于是,\forall 1 \le i \le N,我们可以先求出 \hat{f}(i)\hat{g}(i),这个如果你 f,g 的复杂度都是 O\left(1\right) 的话总的复杂度大致是 O\left(n \log n \right) 的。然后,类似 FFT 和 NTT,我们把 \hat{h}(i) 相应的值算出来。现在,我们只需要考虑怎么靠着我们求出来的 \hat{h}1 \sim N 处的取值算出 h1 \sim N 处的取值。

这里的话就有不少做法了。

比方说你可以莫反,显然 h(n)=\sum\limits_{d \mid n}\mu(\frac{n}{d})\hat{h}(d),直接算即可。复杂度大概是 O\left(n \log n \right)

当然还有不需要求 \mu 的做法。我们回忆一下,\hat{h}(n)=\sum\limits_{n \mid k}h(k)=\sum\limits_{d=1}^{\lfloor\frac{N}{n}\rfloor}h(nd)=h(n)+\sum\limits_{d=2}^{\lfloor\frac{N}{n}\rfloor}h(nd)。于是,h(n)=\hat{h}(n)-\sum\limits_{d=2}^{\lfloor\frac{N}{n}\rfloor}h(nd)。于是你就从远处开始往近处求值就行了。时间复杂度还是 O\left(n \log n \right)

如果你问我 \rm{lcm} 的话其实也差不多,手推一下就行了。

这里贴一份实现。