一类积性函数的前缀和---刷题记录

Posted NeighThorn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一类积性函数的前缀和---刷题记录相关的知识,希望对你有一定的参考价值。

题目来源于糖教主浅谈一类积性函数的前缀和...

51Nod 1244 莫比乌斯函数之和

考虑$\mu(x)$的性质:$[n==1]=\sum _{d\mid n} \mu(d)$

可以用上面哪个公式来推导:

$f(n)=\sum _{i=1}^{n}$

$1=\sum _{i=1}^{n} [i==1]$

$=\sum _{i=1}^{n} \sum _{d\mid i} \mu (d)$

$=\sum _{\frac{i}{d}=1}^{n} \sum _{d=1}^{\frac{n}{\frac{i}{d}}} \mu (d)$

$=\sum _{i=1}^{n}\sum _{d=1}^{\frac{n}{i}} \mu(d)$

$=\sum _{i=1}^{n}f(\frac{n}{i})$

$f(n)=1-\sum _{i=2}^{n} f(\frac{n}{i})$

然后,我们预处理出前$\sqrt{n}$个的$f(x)$,然后对于大于$\sqrt{n}$的数的答案,分块递归计算...

复杂度的证明请见糖教主的文章...

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
//by NeighThorn
using namespace std;

const int maxn=5000000+5;

int cnt,mu[maxn],pri[maxn],vis[maxn];
long long n,m,f[maxn];

map<long long,long long> mp;

inline void prework(void){
	mu[1]=1;
	for(int i=2;i<=5000000;i++){
		if(!vis[i])
			vis[i]=1,pri[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&1LL*i*pri[j]<=5000000;j++){
			vis[i*pri[j]]=1;
			if(i%pri[j]==0){
				mu[i*pri[j]]=0;
				break;
			}
			mu[i*pri[j]]=-mu[i];
		}
	}
	for(int i=1;i<=5000000;i++) f[i]=f[i-1]+mu[i];
}

inline long long calc(long long x){
	if(x<=5000000) return f[x];
	if(mp.find(x)!=mp.end()) return mp[x];
	long long ans=1;
	for(long long i=2,r;i<=x;i=r+1){
		r=x/(x/i);
		ans-=calc(x/i)*(r-i+1);
	}
	return mp[x]=ans;
}

signed main(void){
	prework();scanf("%lld%lld",&n,&m);
	printf("%lld\n",calc(m)-calc(n-1));
	return 0;
}

  

51Nod 1239 欧拉函数之和 

和上面的题目差不多...

这次利用的是$\phi(x)$的这个性质:$\sum _{d\mid n} \phi(d)=n$

$\phi(n)=n-\sum _{d\mid n  d<n}\phi(d)$

$f(n)=\sum _{i=1}^{n} (i-\sum _{d\mid i  d<i} \phi(d))$

$=\frac{n(n+1)}{2}-\sum _{i=2}^{n} \sum _{d\mid i  d<i} \phi(d)$

$=\frac{n(n+1)}{2}-\sum _{i=2}^{n}\sum _{d=1}^{\frac{n}{i}} \phi(d)$

$=\frac{n(n+1)}{2}-\sum _{i=2}^{n} f(\frac{n}{i})$

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
//by NeighThorn
using namespace std;

const int maxn=5000000+5,mod=1e9+7;

int cnt,f[maxn],pri[maxn],phi[maxn],vis[maxn];
long long n;

map<long long,int> mp;

inline int mul(long long x,long long y){
	int res=0;x%=mod;
	while(y){
		if(y&1) res=(res+x)%mod;
		x=(x+x)%mod,y>>=1;
	}
	return res;
}

inline int power(int x,int y){
	int res=1;
	while(y){
		if(y&1) res=1LL*res*x%mod;
		x=1LL*x*x%mod,y>>=1;
	}
	return res;
}

inline void prework(void){
	phi[1]=1;
	for(int i=2;i<=5000000;i++){
		if(!vis[i])
			pri[++cnt]=i,vis[i]=1,phi[i]=i-1;
		for(int j=1;j<=cnt&&1LL*i*pri[j]<=5000000;j++){
			vis[i*pri[j]]=1;
			if(i%pri[j]==0){
				phi[i*pri[j]]=phi[i]*pri[j];
				break;
			}
			phi[i*pri[j]]=phi[i]*(pri[j]-1);
		}
	}
	for(int i=1;i<=5000000;i++) f[i]=(f[i-1]+phi[i])%mod; 
}

inline int calc(long long n){
	if(n<=5000000) return f[n];
	if(mp.find(n)!=mp.end()) return mp[n];
	int ans=mul(mul(n,n+1),power(2,mod-2));
	for(long long i=2,r;i<=n;i=r+1){
		r=n/(n/i);
		ans=(ans-1LL*calc(n/i)*((r-i+1)%mod)%mod+mod)%mod;
	}
	return mp[n]=ans;
}

signed main(void){
	prework();scanf("%lld",&n);
	printf("%lld\n",calc(n));
	return 0;
}

  

 

以上是关于一类积性函数的前缀和---刷题记录的主要内容,如果未能解决你的问题,请参考以下文章

算法专题积性函数

模板杜教筛(Sum)

min_25筛

min_25筛

LibreOJ - 6053(积性函数前缀和,pn筛角度)

LibreOJ - 6053(积性函数前缀和,pn筛角度)