注意本文中用的字母可能和其他博客中有區別。
黎曼zeta函數\(\zeta(x)=\sum_{n\ge 1} \frac{1}{n^x}\)。
手寫時本人喜歡寫成\(z\)(因為\(\zeta\)太難寫),但是在博客中還是正式點吧。
參考資料:
https://zhuanlan.zhihu.com/p/50817119
https://blog.csdn.net/luositing/article/details/109204796
形式
狄利克雷生成函數(DGF)形式:
以下默認\(n\ge 1,n\in Z\)。
不難發現在這個形式下,如果有\(H(x)=F(x)G(x)\),那么有\(h_n=\sum_{d|n} f_dg_{\frac{n}{d}}\),也就是說\(h\)是\(f,g\)的狄利克雷卷積。
常用數論函數的DGF
當\(\forall n,f_n=1\)時,\(F(x)=\zeta(x)\)。
根據數論函數的基本性質,可以得到
當\(f_n=\mu(n)\)時:根據\(\mu\)的定義可得\(F(x)=\prod_p1-p^{-x}\)。於是有\(F(x)=\frac{1}{\zeta(x)}\)。
當\(f_n=n\)時:\(F(x)=\prod_p\sum_{i\ge 0}\frac{p^i}{p^{ix}}=\prod_p\frac{1}{1-p^{-(x-1)}}=\zeta(x-1)\)。
當\(f_n=\phi(n)\)時:\(F(x)=\prod_p 1+\frac{p-1}{p}\sum_{i\ge 1}\frac{1}{p^{i(x-1)}}=\prod_p\frac{1-p^{-x}}{1-p^{-(x-1)}}=\frac{\zeta(x-1)}{\zeta(x)}\)。
當\(f_n=\sigma_0(n)\)時:\(F(x)=\zeta^2(x)\)。
當\(f_n=\sigma_1(n)\)時:\(F(x)=\zeta(x)\zeta(x-1)\)。
通過這些東西,可以很直觀地發現一些性質,什么\(I*\mu=e\),\(I*\phi=id\),\(\sigma_0*\phi=\sigma_1\)之類的。
DGF的運算
加減不說,乘法朴素\(O(n\ln n)\),用類似高維前綴和的方法好像可以做到\(O(n\ln \ln n)\)。
除法:假如有\(H(x)=F(x)G(x)\),已知\(H(x),F(x)\),則有\(h_n=\sum_{d|n,d>1}f_dg_{\frac{n}{d}}+f_1g_n\),移項得到\(g_n\)。時間\(O(n\ln n)\)。
求導、積分:
\(\ln n\)似乎不好處理。注意到求導和積分往往是成對存在的,不妨找個\(\ln n\)的替代品。定義\(c(n)\)表示\(n\)的可重質因子個數,用它來替代。這個函數也滿足\(\ln\)的部分性質:\(c(1)=0,c(ab)=c(a)+c(b)\)。
它好像沒有復刻\(ln\)的全部性質,為什么是對的?以下談談本人的感性理解:
當然如果有高深的高等數學知識可以略過我認為這個疑問都來自於不同質數之間的關系,比如\(\ln 2\neq \ln 3\),然而這里\(c(2)=c(3)=1\)。
但是其實可以把每個質數看成一個維度,其指數為在這一維上的坐標,於是每個數都相當於無限維空間中的一個點。
我們可以把所有數重標號一下,把質因子\(2\)全部換成\(3\),把質因子\(3\)全部換成\(2\),相當於兩個維度換一下。如果\(\ln 2\neq\ln 3\)對求導積分之后的結果有影響,不符合對稱性,矛盾。
所以並不需要滿足\(\ln 2\neq \ln 3\)。
於是變成這樣:
求\(\ln\):\(\ln F(x)=\int \frac{F'(x)}{F(x)}dx\)。
求\(exp\):設\(G(x)=e^{F(x)}\),則\(G'(x)=F'(x)G(x)\)。把式子拆開,用類似除法的方式做就行了。
例題
PE639(題解):https://www.cnblogs.com/jz-597/p/14402383.html
LOJ6713. 「EC Final 2019」狄利克雷 k 次根 加強版。下面貼代碼:
using namespace std;
#include <bits/stdc++.h>
#define N 1000005
#define mo 998244353
#define ll long long
ll qpow(ll x,ll y=mo-2){
ll r=1;
for (;y;y>>=1,x=x*x%mo)
if (y&1)
r=r*x%mo;
return r;
}
int n,k;
ll g[N],f[N];
int p[N],np;
bool inp[N];
int c[N],inv[N];
void init(){
c[1]=0;
for (int i=2;i<=n;++i){
if (!inp[i])
p[++np]=i,c[i]=1;
for (int j=1;j<=np && i*p[j]<=n;++j){
inp[i*p[j]]=1;
c[i*p[j]]=c[i]+1;
if (i%p[j]==0)
break;
}
}
inv[1]=1;
for (int i=2;i<=n;++i)
inv[i]=(ll)(mo-mo/i)*inv[mo%i]%mo;
}
void check(ll h[],ll f[],ll g[]){
static ll t[N];
for (int i=1;i<=n;++i)
for (int j=1;i*j<=n;++j)
(t[i*j]+=f[i]*g[j])%=mo;
for (int i=1;i<=n;++i)
if (t[i]!=h[i]){
printf("****\n");
exit(0);
}
}
void getdiv(ll h[],ll f[],ll g[]){
static ll d[N];
memset(d,0,sizeof(ll)*(n+1));
ll t=qpow(g[1]);
for (int i=1;i<=n;++i){
d[i]=(d[i]+mo+f[i])*t%mo;
for (int j=2;i*j<=n;++j)
(d[i*j]-=d[i]*g[j])%=mo;
}
memcpy(h,d,sizeof(ll)*(n+1));
check(f,g,h);
}
void getln(ll f[],ll g[]){
static ll g_[N],_f[N];
for (int i=1;i<=n;++i)
g_[i]=g[i]*c[i]%mo;
getdiv(_f,g_,g);
f[1]=0;
for (int i=2;i<=n;++i)
f[i]=_f[i]*inv[c[i]]%mo;
}
void getexp(ll g[],ll f[]){
static ll d[N];
memset(d,0,sizeof(ll)*(n+1));
assert(f[1]==0);
d[1]=1;
for (int j=2;j<=n;++j)
(d[j]+=c[j]*f[j])%=mo;
for (int i=2;i<=n;++i){
d[i]=d[i]*inv[c[i]]%mo;
for (int j=2;i*j<=n;++j)
(d[i*j]+=c[j]*f[j]%mo*d[i])%=mo;
}
memcpy(g,d,sizeof(ll)*(n+1));
}
void getpow(ll h[],ll g[],ll t){
static ll d[N];
getln(d,g);
for (int i=1;i<=n;++i)
d[i]=d[i]*t%mo;
getexp(h,d);
}
int main(){
// freopen("in.txt","r",stdin);
scanf("%d%d",&n,&k);
init();
for (int i=1;i<=n;++i)
scanf("%lld",&g[i]);
getpow(f,g,qpow(k));
for (int i=1;i<=n;++i)
printf("%lld ",f[i]);
// printf("\n");
// getpow(g,f,k);
// for (int i=1;i<=n;++i)
// printf("%lld ",g[i]);
// printf("\n");
return 0;
}