今天我想跟大家分享一下我对欧拉函数的理解:

1∼N 中与 N 互质的数的个数被称为欧拉函数,记为ϕ(N)。
若在算数基本定理中,N=p^a1p^a2…pa^m,则:
ϕ(N) = N×(p1−1)/p1×(p2−1)/p2×…×(pm−1)/pm;

求法大概就是这样了,我下面将给出求单个欧拉函数的核心代码

int m=n;
for(int i=2;i<=sqrt(n);i++)
		{
			if(n%i==0)
				m=m*(i-1)/i;
			while(n%i==0) n/=i;
		}
		if(n!=1) m=m*(n-1)/n;

有时候我们往往不是只求一个数的欧拉函数,这时候我们就需要像求素数那样预处理出来素数表。

下面引入一道题,代码上将对一些重难点进行解析

给定一个正整数 nn,求 1∼n 中每个数的欧拉函数之和。

输入格式

共一行,包含一个整数 n。

输出格式

共一行,包含一个整数,表示 1∼n 中每个数的欧拉函数之和。

数据范围

1≤n≤10^6

输入样例:

6

输出样例:

12

代码分析

为什么欧拉系统创建docker只会有tcp6没有tcp4_ios

 

#include<bits/stdc++.h>
using namespace std;
int n;
int prime[1000002],o[1000002];
bool vis[1000002];
void init()
{
	o[1]=1;
	int cnt=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			prime[cnt++]=i;
			o[i]=i-1;
		}
		for(int j=1;j<cnt&&i*prime[j]<=n;j++)
		{
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				o[i*prime[j]]=o[i]*prime[j];
				break;
			}
			else
				o[i*prime[j]]=o[i]*(prime[j]-1);
		}
	}
}
int main()
{
	cin>>n;
	init();
	long long ans=0;
	for(int i=1;i<=n;i++)
		ans+=o[i];
	cout<<ans;
	return 0;
}

以上是求欧拉函数的两个裸模板,下面将给出两个实际的欧拉函数应用实例:

Longge's problem:

题意:数论只会GCD,现在SonG给你一个n很大,他现在想知道1到n的所有数字与n的最大公约数之和。

多组输入
每一组一行一个n (1<n<2^31)

输出一行一个数,表示你的答案

示例

输入


2 6


输出


3 15


我们现在来对这道题目进行分析:

不妨假设(i,n)=p,那么必有(i/p,n/p)=1;即每一个与n/p互质的数都对应着一个i/p,也就是对应着一个i,那么题目将转化为求n/p的欧拉函数与i的乘积和。

下面是具体代码:

#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
ll euler(int x)//求单个函数欧拉函数的基本模板
{
    int res=x;
    for(int i=2;i<=sqrt(x);i++)
        if(x%i==0)
        {
            res=res/i*(i-1);
            while(x%i==0) x/=i;
        }
    if(x>1)res=res/x*(x-1);
    return res;
}
int main()
{
	ll n,ans,i;
    while(~scanf("%lld",&n))
    {
        ans=0;
        for(i=1;i<sqrt(n);i++)
		if(n%i==0)
            ans+=i*euler(n/i)+n/i*euler(i);//n=i*(n/i)
        if(i*i==n) ans+=i*euler(i);//特判一下平方数的情况
        printf("%lld\n",ans);
    }
    return 0;
}

下面这个例子将用到筛法预处理出来欧拉函数表

Farey Sequence

题意:众所周知,小涛涛学长是数论的king,artist今天去请教了他一个问题,小涛涛学长读完题目后,觉得太简单了,对artist十分失望,随即离开,只留下一个背影,artist向你求助,你能帮帮她吗? 有一个数列叫做dxl数列,对任意n,DXLn代表一个包含不可简化(不可约分)的有理分数a/b的序列,满足0 < a < b <= n且gcd(a,b)=1,序列中的分数按照递增顺序排列。 前几个DXLn为:
DXL2 = {1/2}
DXL3 = {1/3, 1/2, 2/3}
DXL4 = {1/4, 1/3, 1/2, 2/3, 3/4}
DXL5 = {1/5, 1/4, 1/3, 2/5, 1/2, 3/5, 2/3, 3/4, 4/5}
给定n,你的任务是计算序列DXLn的大小(即含有多少个分数)。
注意:这道题不支持头文件bits/stdc++.h!

输入:多个测试样例。 每个样例只有一行,是一个正整数n(2<=n<=1e6)。每组样例之间没有空行。样例以一个0为结束,遇到这个0时不需输出。

输出:对每个样例,你需要输出一行,包含N(n)----DXLn数列中元素的个数。

样例:

输入:


2 3 4 5 0


输出:


1 3 5 9


分析:这道题目中要求分子与分母互质,由此我们可以想到用欧拉函数,当分子为n时,分子m必须要满足(m,n)=1,容易知道满足这样条件的分子一共会有ϕ(n)个,那么题意将会转化为求2~n的所有数的欧拉函数和,下面是代码:

#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e6+10;
bool vis[N+10];
ll prime[N+10],phi[N+10],cnt;
void init()
{
	phi[1]=1;
	for(int i=2;i<=N;i++)
	{
		if(!vis[i])
		{
			prime[++cnt]=i;
			phi[i]=i-1;
		}
		for(int j=1;j<=cnt&&i*prime[j]<=N;j++)
		{
			vis[i*prime[j]]=true;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}
int main()
{
	int n;
	init();
	while(scanf("%d",&n)!=EOF)
	{
		if(!n) break;
		ll ans=0;
		for(int i=2;i<=n;i++) ans+=phi[i];
		printf("%lld\n",ans);
	}
	return 0;
}

总结:一般涉及到互质问题时可能会用到欧拉函数。