DGF 不存在了:Dirichlet 卷积的 n(loglogn)^2 做法
开篇声明一下,本文是对 _fewq
集合
定义函数
Square free 处的 Dirichlet 卷积计算
对于数列
这两种变换都是可逆的,我们这里只关心
形象地说,
那么我们就有如下定理:
如果令
这恰是 sqaure free 上的 Dirichlet 卷积。
而变换
补全其他部分的 Dirichlet 卷积
对于正整数
记
然后去计算
总的来说,我们只需枚举所有可能的
一个代码实现:
#include <iostream>
#include <cstdio>
#include <cmath>
#define ll long long
#define int unsigned int
using namespace std;
const int Mx = 1e6 + 10, Nx = log(Mx) / log(2);
bool vis[Mx], squarefree[Mx];
int prime[Mx], tot;
int omega[Mx]; // for square free
void sieve(){
squarefree[1] = 1;
for(int i = 2; i < Mx; ++i){
if(!vis[i]) prime[++tot] = i, squarefree[i] = omega[i] = 1;
for(int j = 1; j <= tot && prime[j] * i < Mx; ++j){
vis[i * prime[j]] = true;
if(i % prime[j] == 0) break;
omega[i * prime[j]] = omega[i] + 1;
squarefree[i * prime[j]] = squarefree[i];
}
}
}
void FDT(int F[][Nx], int n){ // fast dirichlet transform
for(int i = 1; prime[i] <= n && i <= tot; ++i){
int p = prime[i];
for(int j = n / p; j; --j) if(squarefree[j * p]){
for(int r = 0; r <= omega[j]; ++r) F[j * p][r] += F[j][r];
}
}
}
void IFDT(int F[][Nx], int n){ // inverse fast dirichlet transform
for(int i = 1; prime[i] <= n && i <= tot; ++i){
int p = prime[i];
for(int j = 1; j <= n / p; ++j) if(squarefree[j * p])
for(int r = 1; r <= omega[j]; ++r) F[j * p][r - 1] -= F[j][r];
}
}
int F[Mx][Nx], G[Mx][Nx], H[Nx];
void mul(int f[], int g[], int h[], int n){ // h = f * g for square free
for(int i = 1; i <= n; ++i) if(squarefree[i]){
F[i][omega[i]] = f[i], G[i][omega[i]] = g[i];
for(int r = 0; r < omega[i]; ++r) F[i][r] = G[i][r] = 0;
}
FDT(F, n), FDT(G, n);
for(int i = 1; i <= n; ++i) if(squarefree[i]){
for(int r = 0; r <= omega[i]; ++r) H[r] = 0;
for(int r = 0; r <= omega[i]; ++r) for(int s = omega[i] - r; s <= omega[i]; ++s)
H[r + s - omega[i]] += F[i][r] * G[i][s];
for(int r = 0; r <= omega[i]; ++r) F[i][r] = H[r];
}
IFDT(F, n);
for(int i = 1; i <= n; ++i) h[i] = F[i][0];
}
int gcd(int x, int y){ return y ? gcd(y, x % y) : x;}
int f[Mx], g[Mx], h[Mx];
int fd[Mx], gd[Mx], hd[Mx];
void dfs(int A, int B, int pos, int n){ // A * B is PN
if((ll)prime[pos] * prime[pos] > n / A / B || pos > tot){
int m = n / A / B, pn = A * B;
// 下面用 gcd 来写其实会使复杂度变劣
// 严格的写法需要按 fewq 那种写法,每次 dfs 到下一层时就分离掉不互质的部分
// 或者使用快速 gcd,毕竟这样不需要额外的空间,对常数也许有帮助
for(int i = 1; i <= m; ++i){
if(gcd(i, pn) == 1) fd[i] = f[i * A], gd[i] = g[i * B];
else fd[i] = gd[i] = 0;
}
mul(fd, gd, hd, m);
for(int i = 1; i <= m; ++i) h[i * pn] += hd[i];
return;
}
dfs(A, B, pos + 1, n);
int m = n / A / B / prime[pos] / prime[pos];
int prod = prime[pos] * prime[pos];
for(int i = 2; m; ++i, m /= prime[pos], prod *= prime[pos])
for(int j = 0, k = 1; j <= i; ++j, k *= prime[pos])
dfs(A * k, B * (prod / k), pos + 1, n);
}
signed main(){
sieve();
int n;
cin >> n;
for(int i = 1; i <= n; ++i) cin >> f[i];
for(int i = 1; i <= n; ++i) cin >> g[i];
dfs(1, 1, 1, n);
for(int i = 1; i <= n; ++i) cout << h[i] << ' ';
return 0;
}
注:里面调用 gcd
的地方可以线性预处理,
我们得到了什么
结合我之前的 DGF 牛顿迭代理论,我们可以得到以下问题的
- DGF 乘法逆
- DGF ln / exp
- DGF 快速幂 / 开根
DGF 或将焕发第二春!