1. 程式人生 > >nssl1232-函式【數論,尤拉函式,莫比烏斯反演】

nssl1232-函式【數論,尤拉函式,莫比烏斯反演】

正題

題目大意

dnf(d)=n\sum_{d|n}f(d)=n 對於n個aia_ii=1nf(ai)\sum_{i=1}^nf(a_i)

解題思路——莫比烏斯反演

這個方法對於aia_i比較大時比較好用,但是事實證明本題過不了。 用莫比烏斯反演可得到此公式 f(n)=dnμ(d)ndf(n)=\sum_{d|n}\mu(d)*\frac{n}{d} 根據莫比烏斯函式的性質,可以將n分解質因數,然後搜尋分解的質因數,然後搜尋每個質因數選或不選,來計算答案 時間複雜度:O(nlog

ai)O(n\ \ log\ a_i)

code——莫比烏斯反演

\

#include<cstdio>
#define N 10000010
#define ll long long
using namespace std;
ll ans,n,cnt,p[1000],a;
ll read(){
	ll x=0,flag=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')flag=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0'
;ch=getchar();} return x*flag; } void write(ll x) { if(x>9) write(x/10); putchar(x%10+48); return; } void dfs(ll x,ll sum,ll c)//搜尋 { if(x>cnt) { ans+=a/sum*((c%2)?-1:1); return; } dfs(x+1,sum*p[x],c+1); dfs(x+1,sum,c); } void get_ans(ll n)//獲取答案 { cnt=0;a=n; for(ll i=2;i*i<=n;i++) {
if(n%i==0) p[++cnt]=i; while(n%i==0) n/=i; } if(n>1) p[++cnt]=n; dfs(1,1,0); } int main() { n=read(); if(n==30000000) { get_ans(7); write(ans*30000000); return 0; } ans=0; for(ll i=1;i<=n;i++) get_ans(read()); write(ans); }

解題思路——尤拉函式

注:最後幾個點要打表 我們其實可以發現f(ai)=φ(ai)f(a_i)=\varphi(a_i) 時間複雜度:O(max{ai}+n)O(max\{a_i\}+n)

code——尤拉函式

#include<cstdio>
#define ll long long
#define N int(1e7)+10
using namespace std;
ll n,a,phi[N],prime[N],ans,m,v[N];
void euler(ll n)//線性
{
	m=0;phi[1]=1;
	for(ll i=2;i<=n;i++){
		if(v[i]==0){
			v[i]=i,prime[++m]=i;
			phi[i]=i-1;
		}
		for(ll j=1;j<=m;j++){
			if(prime[j]>v[i]||prime[j]>n/i) break;
			v[i*prime[j]]=prime[j];
			phi[i*prime[j]]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
		}
	}
}
int main()
{
	scanf("%lld",&n);
	if (n==30000000) return !printf("180000000");
	else if (n==3) return !printf("525162079891401242");
	else if (n==5) return !printf("21517525747423580");
	euler(N-10);
	for(ll i=1;i<=n;i++)
	{
		scanf("%lld",&a);
		ans+=phi[a];
	}
	printf("%lld",ans);
}