以下若无特殊说明,皆认为 \(a_i\) 已知,\(b_i\) 非已知。
前缀和
设有:
\[ b_n=\sum\limits_{i|n}a_i \]显然通过枚举我们可以用 \(O(\sum\left\lfloor \frac{n}{i} \right\rfloor=n\log n)\) 的时间复杂度来做这个事情。我们考虑有没有更为优秀的算法。
根据唯一分解定理,我们有:
\[ n=\prod p_i^{\alpha_i} \]那么其实我们可以用 \((\alpha_1,\alpha_2,...)\) 去表示 \(n\)。所以上面的这些东西我们可以看做是一个高维的前缀和。类比做二维前缀和我们可以先通过对第一维做前缀和然后再对第二维做前缀和得到,高维前缀和我们可以通过对每一维做前缀和的得到最终结果。
代码
for(int i=1;i<=tail&&Prime[i]<=n;i++)
for(int j=1;j*Prime[i]<=n;j++) a[j*Prime[i]]+=a[j];
不难发现复杂度和埃氏筛的复杂度相同,为 \(O(n\log n)\)
Dirchlet 后缀和
我们需要求
\[ b_i=\sum\limits_{i|n}a_n \]显然做后缀和只需要把刚刚那个反过来做就可以。
for(int i=1;i<=tail&&Prime[i]<=n;i++)
for(int j=n/Prime[i];j>=1;j--) a[j]+=a[j*Prime[i]];
倒推 Dirichlet 前缀和
\[ a_n=\sum\limits_{i|n}b_i \]这就相当于告诉我们前缀和结果让我们求每一个位置的元素,我们类比二维前缀和做法,不难得出结论。
for(int i=1;i<=tail$$Prime[i]<=n;i++)
for(int j=n/Prime[i];j>=1;j--) a[j*Prime[i]]-=a[j];
倒推 Dirichlet 前缀和
\[ a_i=\sum\limits_{i|n}b_n \]
for(int i=1;i<=tail&&Prime[i]<=n;i++)
for(int j=1;j*Prime[i]<=n;j++) a[j*Prime]
只要把这个东西高维前缀和的本质说清楚了,其余就很好理解。