• 狄利克雷生成函数


    注意本文中用的字母可能和其他博客中有区别。

    黎曼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)形式:

    [F(x)=sum_{nge 1}frac{f_n}{n^x}。 ]

    以下默认(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))

    根据数论函数的基本性质,可以得到

    [zeta(x)=prod_p1+p^{-x}+p^{-2x}+dots=prod_pfrac{1}{1-p^{-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))

    求导、积分:

    [(frac{f_n}{n^x})'=-ln nfrac{f_n}{n^x}\ int frac{f_n}{n^x}dx=-frac{1}{ln n}frac{f_n}{n^x} ]

    (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)

    于是变成这样:

    [(frac{f_n}{n^x})'=c(n)frac{f_n}{n^x}\ int frac{f_n}{n^x}dx=frac{1}{c(n)}frac{f_n}{n^x} ]

    (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;
    }
    
  • 相关阅读:
    关于开发 Web AI 的思考(kendryte K210)
    怪不得我说,这几个月的代码数据都跑哪里去了....
    在 Android 上使用蓝牙作为主机进行一对多从机传输数据的实测,理论 5
    写了一下 micropython 的文件系统单元测试
    mark 自己未来要写一下,蓝牙主机一对多从机和 K210 的网络通信优化过程。
    VUE实现Studio管理后台(五):手风琴式折叠组件(Accordion)
    VUE实现Studio管理后台(四):状态模式实现窗口停靠,灵动、自由
    VUE实现Studio管理后台(三):支持多语言国际化(vue-i18n)
    VUE实现Studio管理后台(二):Slot实现选项卡tab切换效果,可自由填装内容
    VUE实现Studio管理后台(一):鼠标拖放改变窗口大小
  • 原文地址:https://www.cnblogs.com/jz-597/p/14403977.html
Copyright © 2020-2023  润新知