欧拉函数笔记

首先,我们先来了解一下欧拉函数的定义

定义

  • 在数论,对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目.
  • 一组数 \({x_1,x_2,x_3,…,x_s}\) 称为是模 \({m}\) 的既约剩余系(简称缩系),如果对 \({\forall j,s \in \mathbb Z,1 \le j \le s,(x_j,m)=1}\) ,并定义 \({\phi (m)=s}\) ,即 \({\{1,2,…,m\}}\) 中与 \({m}\) 互素的数的个数,称为欧拉函数.
  • 特别地, \({\phi(1)=1}\) .
  • 而对于 \({m>1}\)\({\phi(m)}\) 就是 \({1,2,…,m-1}\) 中与 \({m}\) 互素的数的个数.
  • 对于 \({p \in \{ }\) 素数 \({ \} }\) ,显然有 \({\phi(p)=p-1}\) .

好,相信这些大家都能理解。(大概)

不懂也没事,

接着往下看……

通式

1.对于一个正整数 \({n}\) ,分解质因子得 \({n = p_1^{x_1} p_2^{x_2} … p_k^{x_k} }\) ,有 \({ \phi(n) = n ( 1 - \frac{1}{p_1} ) ( 1 - \frac{1}{p_2} ) … (1 - \frac{1} {p_k}) }\)


证明

首先, \({ \phi (n)}\) 表示的是 \({n}\) 以内和 \({n}\) 互质的数,对吧?

总共有 \({n}\) 个数,减去和 \({n}\) 不互质的,剩下的不就是互质的个数了吗?

不互质的好像也挺好求,

\({n}\) 的质因子的数不就和 \({n}\) 不互质吗?

所以在 \({n}\) 以内找出是 \({n}\) 质因子的倍数的个数

也就是: \({ \frac{n} {p_1} + \frac{n} {p_2} + … \frac{n}{p_k}}\) 对吗?

……

不对!

可以发现可能会有一些数被重复算了,比如:6 就同时是 2,3 的倍数。

所以我们还要把多出来的次数减去…

也就是容斥原理

最后变式可得到原式.

Code:

long long getPhi(int n) {
	long long ans=n;
	for(int i=2;i*i<=n;i++)
		if(n%i==0) {
			ans=ans*(i-1)/i;
			while(n%i==0)
				n/=i;
		}
	if(n>1) ans=ans*(n-1)/n;
	return ans;
}



欧拉函数与线性筛


然而,在实际中要使用很多个欧拉函数的时候

我们也不可能一个个用这个通式求吧?

时间复杂度太大了。

有个东西叫线性筛……


怎么推?


要知道,欧拉函数还是积性函数.

对于, \({gcd(m,n)=1}\) , 有 \({\phi(mn)=\phi(m)*\phi(n)}\) .

它是可以一路推过去的。


那么对于一个质数 \({p}\) ,和一个正整数 \({n}\) ,有两种情况:

1. \({p}\)\({n}\) 互质:

显然可以直接推: \({\phi(pn)=\phi(n)*\phi(p)}\)\({\phi(n)*(p-1)}\)

2. \({p}\)\({n}\) 不互质:

因为 \({p \in }\) 质数,所以 \({p}\) 就是 \({n}\) 的一个质因子.

所以 \({pn}\) 的质因子和 \({n}\) 是完全一样的,

所以由欧拉函数通式: \({ \phi(n) = n ( 1 - \frac{1}{p_1} ) ( 1 - \frac{1}{p_2} ) … (1 - \frac{1} {p_k}) }\),就有 \({\phi(pn)=pn( 1 - \frac{1}{p_1} ) ( 1 - \frac{1}{p_2} ) … (1 - \frac{1} {p_k})=p *\phi(n)}\).


利用以上的式子,就可以用线性筛算出 \({1}\)\({n}\) 的欧拉函数。而时间复杂度只有 \({O(n)}\)

Code:

void ini() {
	phi[1]=1;
	memset(isPri,true,sizeof(isPri));
	isPri[1]=false;
	for(int i=2;i<=n;i++) {
		if(isPri[i]) {
			phi[i]=i-1;
			pri.push_back(i);
		}
		for(int j=0;j<pri.size();j++) {
			int x=pri[j]*i;
			if(x>n) break;
			isPri[x]=false;
			if(i%pri[j]!=0) phi[x]=phi[i]*phi[pri[j]];
			else {
				phi[x]=phi[i]*pri[j];
				break;
			}
		}
	}
	return ;
}

定理

  • \({\sum \limits_{d|n} \phi(d) = n}\)

证明1:

我们设一个集合为 \({ \{ \frac{1}{n} , \frac{2}{n} , \frac{3}{n} , … , \frac{n}{n} \} }\)

对于上述集合的每个元素,将其化成最简形式。

设一个最简分数为 \({ \frac{a}{b} }\)

则一定有: \({b|n , (a,b)=1 , a \le b }\)

由第一个式子可知 \({b}\) 就等价于定理式子里的每一个 \({d}\)

再由第二、三个式子可得,每个 \({b}\) 对应的 \({a}\)\({\phi(b)}\) 个。

而它们的总和,也就是集合的元素个数 \({:n}\).

代入 \({b=d}\) ,就化成了原式 \({=n}\).

得证。

证明2:

我们以枚举质因子的方式,将 \({d}\) 变换形式可得下述.

\({\sum \limits_{d|n} \phi(d) = \sum \limits^{a_1}_{i_1=0} \sum \limits^{a_2}_{i_2=0} … \sum \limits^{a_k}_{i_k=0} \phi(p_{1}^{i_1} p_{2}^{i_2} … p_{k}^{i_k} ) }\)

\({\ \ \ \ \ \ \ \ \ \ \ \ \ }\) \({=\sum \limits^{a_1}_{i_1=0} \sum \limits^{a_2}_{i_2=0} … \sum \limits^{a_k}_{i_k=0} [ \phi (p_1^{i_1} p_2^{i_2}…)\times \phi(p_k^{i_k})] }\)

\({\ \ \ \ \ \ \ \ \ \ \ \ \ }\) \({=\sum \limits^{a_1}_{i_1=0} \sum \limits^{a_2}_{i_2=0} … [\phi(p_1^{i_1}p_2^{i_2}…) \times \sum \limits_{i_k=0}^{a_k} \phi (p_k^{i_k})]}\)

\({\ \ \ \ \ \ \ \ \ \ \ \ \ }\) \({=\sum \limits^{a_1}_{i_1=0} \sum \limits^{a_2}_{i_2=0} … [\phi(p_1^{i_1}p_2^{i_2}…) \times p_k^{ak}]}\)

\({\ \ \ \ \ \ \ \ \ \ \ \ \ }\) \({=p_k^{ak}\sum \limits^{a_1}_{i_1=0} \sum \limits^{a_2}_{i_2=0} … [\phi(p_1^{i_1}p_2^{i_2}…) }\)

重复以上过程,最后可得:

\({\sum \limits_{d|n} \phi(d) = p_1^{a_1} p_2^{a_2} …p_k^{a_k} }\)

\({\ \ \ \ \ \ \ \ \ \ \ \ \ }\) \({=n}\)

得证。


  • \({ \phi (a*b) = \phi(a) * \phi(b) * \frac{d}{\phi(d)}}\)
    \({( \ d=gcd(a,b) \ )}\)

证明:

直接代入通式就好了。


现在我们来实际应用一下以上知识。


例题:P1390 公约数的和

题目描述:

给定 \({n}\) ,求 \({\sum \limits^{n}_{i=1} \sum \limits^{n}_{j=i+1} gcd(i,j).}\)

输入格式

输入只有一行一个整数,表示 \({n}\)

输出格式

输出一行一个整数表示答案。

说明/提示

\({2 \le n \le 2×10^6}\)


Code:

#include<bits/stdc++.h>
using namespace std;

const int maxn=2000000+5;

int n;
long long phi[maxn];
bool isPri[maxn];
vector<int>pri;
long long ans=0;

void ini() {
//	phi[1]=1;
   memset(isPri,true,sizeof(isPri));
   isPri[1]=false;
   for(int i=2;i<=n;i++) {
   	if(isPri[i]) {
   		phi[i]=i-1;
   		pri.push_back(i);
   	}
   	for(int j=0;j<pri.size();j++) {
   		int x=pri[j]*i;
   		if(x>n) break;
   		isPri[x]=false;
   		if(i%pri[j]!=0) phi[x]=phi[i]*phi[pri[j]];
   		else {
   			phi[x]=phi[i]*pri[j];
   			break;
   		}
   	}
   }
   return ;
}

int main() {
   cin>>n;
   ini();
   for(int i=1;i*i<=n;i++) {
   	ans+=phi[i]*i;
   	for(int j=i+1;i*j<=n;j++)
   		ans+=phi[i]*j+phi[j]*i;
   }
   cout<<ans;
   return 0;
}

题解

【例题】P2568 GCD

题目描述

给定正整数 \(n\),求 \(1\le x,y\le n\)\(\gcd(x,y)\) 为素数的数对 \((x,y)\) 有多少对。

输入格式

只有一行一个整数,代表\(n\)

输出格式

一行一个整数表示答案。

样例 #1

样例输入 #1

4

样例输出 #1

4

提示

样例输入输出 1 解释

对于样例,满足条件的 \((x,y)\)\((2,2)\)\((2,4)\)\((3,3)\)\((4,2)\)


数据规模与约定

  • 对于 \(100\%\) 的数据,保证 \(1\le n\le10^7\)

参考程序:

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e7+5;

int n,prime[700005],cnt=0,phi[maxn];
long long sum[maxn];
bool isPri[maxn];
long long ans=0;

void init() {
	memset(isPri,true,sizeof(isPri));
	isPri[1]=false;
	phi[1]=1;
	for(long long i=2;i<=n;i++) {
		if(isPri[i]) {
			prime[++cnt]=i;
			phi[i]=i-1;
		}
		for(long long j=1;j<=cnt&&prime[j]*i<=n;j++) {
			isPri[prime[j]*i]=false;
			if(i%prime[j]==0) {
				phi[prime[j]*i]=prime[j]*phi[i];
				break;
			}
			else phi[prime[j]*i]=(prime[j]-1)*phi[i];
		}
	}
	for(int i=1;i<=n;i++)
		sum[i]=sum[i-1]+phi[i];
	return ;
}

int main() {
	cin>>n;
	init();
	for(int i=1;i<=cnt;i++)
		ans+=2*sum[n/prime[i]]-1;
	cout<<ans;
	return 0;
}

题解

第6题 GCD(X,N)>=M

给定整数N和M,问题是求有多少个整数X,满足1 <= X <= N且gcd(X,N)> = M。

输入格式

有T组测试数据。1 <= T <= 100

每组测试数据格式如下:

一行,两个整数N和M。 2<=N<=10^9, 1 <= M <= N.

输出格式

共T行,每行一个整数。

输入/输出例子1

输入:

3

1 1

10 2

10000 72

输出:

1

6

260

参考程序:

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int maxn=1e9+5;

int G,n,m;
long long ans;

long long getPhi(int x) {
	int re=x;
	for(int i=2;i*i<=x;i++)
		if(x%i==0) {
			re=re*(i-1)/i;
			while(x%i==0) x/=i;
		}
	if(x>1) re=re*(x-1)/x;
	return re;
}

signed main() {
	cin>>G;
	while(G--) {
		cin>>n>>m;ans=0;
		for(int i=1;i*i<=n&&i<=n/m;i++) {
			if(n%i==0) {
				ans+=getPhi(i);
				if(n/i<=n/m&&n/i!=i) ans+=getPhi(n/i);
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

【例题】第7题 非互质数求和

如果一个正整数x小于N,而且x与N不互质,那么整数x称为“好数”。

给定一个正整数N,求所有的“好数”的和。

如果A,B除1外没有其他的公因数,则称A对B互质。

答案模1000000007。

输入格式

若干行,每行一个整数N(1≤N≤10^9)。

最后一行,N=0表示结束。

输出格式

若干行,每行一个整数。

输入/输出例子1

输入:

3

4

0

输出:

0

2

参考程序:

#include<bits/stdc++.h>
using namespace std;

const long long mod=1e9+7;

long long n,ans;

long long getPhi(long long x) {
	long long re=x;
	for(long long i=2;i*i<=x;i++)
		if(x%i==0) {
			re=re*(i-1)/i;
			while(x%i==0)
				x=x/i;
		}
	if(x>1) re=re*(x-1)/x;
	return re;
}

int main() {
	while(1) {
		scanf("%lld",&n);
		if(n==0) return 0;
		ans=n*(n-1)/2;
		printf("%lld\n",(ans-n*getPhi(n)/2)%mod);
	}
	return 0;
}

题解:

需要证明一个定理:

  • \({i}\)\({n}\) 互质时,\({n-i}\) 也与 \({n}\) 互质

证明:

对于一个 \({i}\)\({n}\) 互质,

则有 \({gcd(i,n)=1}\)

\({gcd(n-i,n)=k}\).

则有 \({ (n-i) \ mod \ k=0 , n \ mod \ k =0}\) .

\({i \ mod \ k =0}\).

\({gcd(i,n)=k}\).

又因为 \({gcd(i,n)=1}\)

所以 \({k=1}\).

得证。


【压轴题】USB的数学题

题目:求 \({F(n)= \sum \limits_{i=1}^{n} \frac{n}{gcd(i,n)}}\) .

输入格式

第一行,一个整数T。表示有T组测试数据。( T <= 1000000 )

接下来有T行,每行一个整数n。( n <= 10000000 )

输出格式

共T行,每行一个整数。

输入/输出例子1

输入:

10

39

12

37

15

91

63

32

87

28

7

输出:

1099

77

1333

147

6751

2623

683

5691

473

43

参考程序:

#include<bits/stdc++.h>
#define int long long
using namespace std;

const long long maxn=1e7;

int G,cnt=0,micnt[maxn+5],mi[maxn+5],zhi[maxn+5];
bool isPri[maxn+5];
long long n,ans,phi[maxn+5],pri[700000+5],sum[maxn+5];

void init() {
	memset(isPri,true,sizeof(isPri));
	phi[1]=1;
	sum[1]=1;
	for(int i=2;i<=maxn;i++) {
		if(isPri[i]) {
			pri[++cnt]=i;
			phi[i]=i-1;
			sum[i]=phi[i]*i+1;
			micnt[i]=1;
			mi[i]=i;
			zhi[i]=i;
		}
		for(int j=1;j<=cnt&&pri[j]*i<=maxn;j++) {
			int ne=pri[j]*i;
			isPri[ne]=false;
			if(i%pri[j]==0) {
				phi[ne]=pri[j]*phi[i];
				mi[ne]=mi[i];
				micnt[ne]=micnt[i]+1;
				zhi[ne]=zhi[i]*mi[i];
				if(zhi[ne]==ne) {
					sum[ne]=1;
					for(int k=1,l=mi[i];k<=micnt[i]+1;k++,l=l*mi[i])
						sum[ne]+=phi[l]*l;
				}
				else sum[ne]=sum[zhi[i]*mi[i]]*sum[i/zhi[i]];
				break;
			}
			else {
				phi[ne]=phi[pri[j]]*phi[i];
				sum[ne]=sum[pri[j]]*sum[i];
				if(pri[j]<mi[i]) mi[ne]=pri[j],micnt[ne]=1,zhi[ne]=pri[j];
				else mi[ne]=mi[i],micnt[ne]=micnt[i],zhi[ne]=zhi[i];
			}
		}
	}
	return ;
}

signed main() {
	init();
	scanf("%d",&G);
	while(G--) {
		scanf("%lld",&n);
		printf("%lld\n",sum[n]);
	}
	return 0;
}

题解

容易发现 \({F(n)= \sum \limits_{d|n} d \phi (d)}\) .

然后我们还容易发现 \({F(n)}\) 是个积性函数.

用线性筛推即可。

注意当 \({i}\)\({pri[j]}\) 为底数的幂时,我们不能靠拆分的方法造互质,这里用第一行的公式直接算出.

posted @ 2023-04-29 07:05  ForBiggerWorld  阅读(55)  评论(0)    收藏  举报