PE639 Summing a multiplicative function

有积性函数\(f(i)\),其中\(f_k(p^e)=p^k,e>0\)。

\(S_k(n)=\sum_{i=1}^nf_k(i)\)

求\(\sum_{k=1}^{50} S_k(10^{12})\)


学习狄利克雷生成函数的时候碰见的例题:https://zhuanlan.zhihu.com/p/50817119

很容易写出生成函数:

\[F(x)=\prod_p(1+\frac{p^k}{p^x}+\frac{p^k}{p^{2x}}+\dots) \]

推一下式子:

\[F(x)=\prod(1+\frac{p^k}{p^x}\frac{1}{1-p^{-x}})\\=\prod(\frac{1+p^{-x}+p^{k-x}}{1-p^{-x}})\\=\prod(\frac{1+p^{-x}+p^{k-x}}{1-p^{-x}}\frac{1-p^{k-x}}{1-p^{k-x}})\\=(\sum\frac{1}{1-p^{k-x}})\prod(1+(p^k-p^{2k})(p^{-2x}+p^{-3x}+\dots))\\=\zeta(x-k)\prod(1+(p^k-p^{2k})(p^{-2x}+p^{-3x}+\dots))\\=(\sum\frac{n^k}{n^x})\prod(1+(p^k-p^{2k})(p^{-2x}+p^{-3x}+\dots)) \]

可以发现右半边的式子中只有powerful number处有值。于是枚举右半边式子中有值的位置,它的贡献就是系数乘上左半边对应的前缀和。powerful number有\(O(\sqrt n)\)个。前缀和可以\(O(k)\)算。

所以理论上时间复杂度是\(O(k^2\sqrt n)\),用家里超慢的机子跑了108s。


using namespace std;
#include <bits/stdc++.h>
#define ll long long
#define mo 1000000007
#define K 55
#define M 1000005
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 p[M],np,c[M];
bool inp[M];
int mu[M];
void initp(int n){
	mu[1]=1;
	for (int i=2;i<=n;++i){
		if (!inp[i])
			p[++np]=i,mu[i]=-1;
		for (int j=1;j<=np && i*p[j]<=n;++j){
			inp[i*p[j]]=1;
			if (i%p[j]==0)
				break;
			mu[i*p[j]]=-mu[i];
		}
	}
}
ll n,m;
int maxk,k;
ll S[K][K],inv[K],sc[K];
int presum(ll n){
	int s=0,r=(n+1)%mo,t=r;
	for (int j=1;j<=k;++j){
		--r;
		t=(ll)t*r%mo;
		s=(s+sc[j]*t)%mo;
//		s=(s+S[k][j]*t%mo*inv[j+1])%mo;
	}
	return s;
}
ll ans;
void dfs(int i,ll v,ll w){
	(ans+=presum(n/v)*w)%=mo;
	for (;i<=np && (__int128)v*p[i]*p[i]<=n;++i){
		ll v_=v*p[i],w_=w*c[i]%mo;
		while ((__int128)v_*p[i]<=n)
			dfs(i+1,v_*=p[i],w_);
	}
}
ll doit(int _k){
	k=_k;
	for (int j=1;j<=k;++j)
		sc[j]=S[k][j]*inv[j+1]%mo;
	for (int i=1;i<=np;++i){
		ll tmp=qpow(p[i],k);
		c[i]=(tmp-tmp*tmp%mo+mo)%mo;
	}
	dfs(1,1,1);
}
int main(){
	freopen("in.txt","r",stdin);
	scanf("%lld%d",&n,&maxk);
	m=sqrt(n);
	while ((m+1)*(m+1)<=n) ++m;
	initp(m);
	S[0][0]=1;
	for (int i=1;i<=maxk;++i)
		for (int j=1;j<=i;++j)
			S[i][j]=(S[i-1][j-1]+j*S[i-1][j])%mo;
	inv[1]=1;
	for (int i=2;i<=maxk+1;++i)
		inv[i]=(mo-mo/i)*inv[mo%i]%mo;
//	doit(maxk);
	for (int i=1;i<=maxk;++i){
		doit(i);
		printf("%d\n",i);
	}
	ans=(ans+mo)%mo;
	printf("%lld\n",ans);
	//ans=797866893
	return 0;
}
上一篇:「学习笔记」多项式Lagrange插值


下一篇:线性筛