注意本文中用的字母可能和其他博客中有区别。
黎曼zeta函数(zeta(x)=sum_{nge 1} frac{1}{n^x})。
手写时本人喜欢写成(z)(因为(zeta)太难写),但是在博客中还是正式点吧。
参考资料:
https://zhuanlan.zhihu.com/p/50817119
https://blog.csdn.net/luositing/article/details/109204796
形式
狄利克雷生成函数(DGF)形式:
以下默认(nge 1,nin 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_psum_{ige 0}frac{p^i}{p^{ix}}=prod_pfrac{1}{1-p^{-(x-1)}}=zeta(x-1))。
当(f_n=phi(n))时:(F(x)=prod_p 1+frac{p-1}{p}sum_{ige 1}frac{1}{p^{i(x-1)}}=prod_pfrac{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(nln n)),用类似高维前缀和的方法好像可以做到(O(nln 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(nln n))。
求导、积分:
(ln n)似乎不好处理。注意到求导和积分往往是成对存在的,不妨找个(ln n)的替代品。定义(c(n))表示(n)的可重质因子个数,用它来替代。这个函数也满足(ln)的部分性质:(c(1)=0,c(ab)=c(a)+c(b))。
它好像没有复刻(ln)的全部性质,为什么是对的?以下谈谈本人的感性理解:
当然如果有高深的高等数学知识可以略过我认为这个疑问都来自于不同质数之间的关系,比如(ln 2 eq ln 3),然而这里(c(2)=c(3)=1)。
但是其实可以把每个质数看成一个维度,其指数为在这一维上的坐标,于是每个数都相当于无限维空间中的一个点。
我们可以把所有数重标号一下,把质因子(2)全部换成(3),把质因子(3)全部换成(2),相当于两个维度换一下。如果(ln 2 eqln 3)对求导积分之后的结果有影响,不符合对称性,矛盾。
所以并不需要满足(ln 2 eq 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("****
");
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("
");
// getpow(g,f,k);
// for (int i=1;i<=n;++i)
// printf("%lld ",g[i]);
// printf("
");
return 0;
}