歐拉函數(匯總&例題)


定義

歐拉函數 $\varphi(n)$表示小於等於$n$的正整數中與$n$互質的數的數目。

 

性質

1、積性函數(證明)。

2、$\varphi(1)=1$(顯然)

3、對於質數$n$,$\varphi(n)=n-1$(顯然)

4、對於質數的冪$n=p^k$(其中$p$為質數,$k$為正整數),$\varphi(n)=p^{k-1}\cdot(p-1)$

證明:

歸納法,在$k=1$時顯然成立,假設當$k$為$k-1$時成立,那么對於將$1,2,...p^k$中每一個數表示為$x\cdot p^{k-1}+d$,其中$0\leq x<p$,$1\leq d\leq p^{k-1}$,若某一個數對$\varphi(n=p^k)$有貢獻,則其$d$的部分一定不含質因子$p$,因而一定對$\varphi(p^{k-1})$有貢獻,所以,恰好每一個對$\varphi(p^{k-1})$有貢獻的數都會對$\varphi(p^k)$有$p$次貢獻,所以有$\varphi(p^k)= \varphi(p^{k-1}) \times p=p^{k-2}\times (p-1)\times p=p^{k-1}\times (p-1)$,得證。

 

計算

不妨設$n=\prod p_i^{t_i}$,其中$p_i$是質數,$t_i$為正整數。

則有$\varphi(n)=n \prod \frac {p_i-1} {p_i}$。

特別的,$\varphi(1)=1$。

證明的話,利用積性函數的性質和性質四組合即可證明。

 

反演

利用歐拉函數本身定義和其一條重要性質$$n=\sum\limits_{d|n} \varphi(d)$$

(其證明涉及到了我的知識盲區)

在莫比烏斯反演中,我們常利用莫比烏斯函數的性質把$$\sum\limits_{x=1}^{n} \sum\limits_{y=1}^{n} [gcd(x,y)=1]$$ 

轉化為$$\sum\limits_{x=1}^n \sum\limits_{y=1}^n \sum\limits_{d|x,d|y} \mu(d)$$

然后進一步改變枚舉項

那么類似的,我們也可以利用歐拉函數的定義將其轉化$$\sum\limits_{x=1}^{n} \sum\limits_{y=1}^{n} [gcd(x,y)=1]\Rightarrow 2( \sum\limits_{i=1}^{n} \varphi(i))-1$$

 

這里兩道利用歐拉函數進行反演的例題

1、BZOJ4804 歐拉心算

$T$組數據,給定$n$求$\sum\limits_{x=1}^{n} \sum\limits_{y=1}^{n} \varphi(gcd(x,y))$,$(T\leq 5000,n\leq 10^7)$

和上文的方法類似

$$\sum\limits_{x=1}^{n} \sum\limits_{y=1}^{n} \varphi(gcd(x,y))$$

$$\Downarrow$$

$$\sum\limits_{d=1}^{n} \varphi(d)\sum\limits_{x=1}^{\lfloor \frac nd \rfloor}\sum\limits_{y=1}^{\lfloor \frac nd \rfloor}[gcd(x,y)=1]$$

$$\Downarrow$$

$$\sum\limits_{d=1}^{n} \varphi(d)(2( \sum\limits_{i=1}^{\lfloor \frac nd \rfloor} \varphi(i))-1)$$這樣只需要對前半部分數論分塊,對后半部分線性篩預處理前綴和即可。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 10000010
using namespace std;
int read(){
	int nm=0,fh=1;char cw=getchar();
	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
	return nm*fh;
}
int n,m,p[M],ph[M],tot,T;
LL G[M];
bool vis[M];
void init(){
	memset(vis,true,sizeof(vis));
	ph[1]=G[1]=1,vis[1]=false;
	for(int i=2;i<M;i++){
		if(vis[i]) ph[i]=i-1,p[++tot]=i;
		for(int j=1;j<=tot&&p[j]*i<M;j++){
			int num=p[j]*i; vis[num]=false;
			if(i%p[j]==0){ph[num]=ph[i]*p[j];break;}
			ph[num]=ph[i]*ph[p[j]];
		}
		G[i]=G[i-1]+ph[i];
	}
}
LL calc(){
	int now=1,RS,D;
	LL ans=0;
	while(now<=n){
		D=n/now,RS=n/D;
		ans+=(G[RS]-G[now-1])*((G[D]<<1)-1);
		now=RS+1;
	}
	return ans;
}
void write(LL x){
	if(x>9) write(x/10);
	putchar(x%10+'0'); 
}
int main(){
	init(),T=read();
	while(T--) n=read(),write(calc()),putchar('\n');
	return 0;
}

2、BZOJ3518 點組計數(權限題)

求$n\times m$方格點陣上任選三點共線的方案數(不同順序屬於同種方案),$n,m\leq 10^5$

將水平和豎直的直線單獨取出計算

對於傾斜的直線,枚舉共線的三點的兩端的橫坐標只差$x$與縱坐標之差$y$。

他們中間的方格點的數量就應該是$gcd(x,y)-1$,接着再把$-1$單獨取出計算,再利用$n=\sum_{d|n} \varphi(d)$這一條性質交換枚舉項進行計算即可。$$ans1=n\cdot C_m^3 + m\cdot C_n^3$$

$$ans2=2\sum\limits_{i=1}^n\sum\limits_{j=1}^m(gcd(i,j)-1)(n-i)(m-j)$$

$$ans2=2(t1-t2),t1=\sum\limits_{i=1}^n\sum\limits_{j=1}^m gcd(i,j)(n-i)(m-j),t2=\frac{n(n-1)}{2}\cdot \frac{m(m-1)}{2}$$

$$n=\sum\limits_{d|n} \varphi(d)$$

$$\Downarrow$$

$$\sum\limits_{i=1}^n\sum\limits_{j=1}^mgcd(i,j)=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{d|i,d|j}\varphi(d)=\sum\limits_{d=1}^{min(n,m)}\varphi(d) \lfloor \frac nd \rfloor \lfloor \frac md \rfloor$$

$$\Downarrow$$

$$t1=\sum\limits_{d=1}^n\varphi(d)\sum\limits_{i=1}^{\lfloor \frac n d \rfloor}(n-i\times d)\sum\limits_{j=1}^{\lfloor \frac m d\rfloor} (m-j\times d)$$仍然只需要常規的數論分塊求解即可。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 50020
#define mod 1000000007
using namespace std;
LL read(){
	LL nm=0,fh=1;char cw=getchar();
	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
	return nm*fh;
}
LL n,m,p[M],ph[M],tot,ans,MAXN;
bool vis[M];
void init(){
	memset(vis,true,sizeof(vis));
	ph[1]=1,vis[1]=false,MAXN=min(n,m);
	for(LL i=2;i<=max(n,m);i++){
		if(vis[i]) ph[i]=i-1,p[++tot]=i;
		for(LL j=1;j<=tot&&p[j]*i<MAXN;j++){
			LL num=p[j]*i; vis[num]=false;
			if(i%p[j]==0){ph[num]=ph[i]*p[j];break;}
			ph[num]=ph[i]*ph[p[j]];
		}
	}
}
LL ari(LL fs,LL ed,LL tot){return ((fs+ed)*(tot)/2)%mod;}
LL calc(){
	LL res=0;
	for(LL d=1;d<MAXN;d++){
		LL t1=ari(n-d,n-((n-1)/d)*d,(n-1)/d);
		LL t2=ari(m-d,m-((m-1)/d)*d,(m-1)/d);
		res+=(t1*t2%mod)*ph[d]%mod;
	}
	return (res<<1)%mod;
}
int main(){
	n=read(),m=read(),init(),ans=calc();
	ans+=mod-(2*((n-1)*n/2)*((m-1)*m/2))%mod;
	if(n>=3) ans+=m*((n*(n-1)*(n-2)/6)%mod)%mod;
	if(m>=3) ans+=n*((m*(m-1)*(m-2)/6)%mod)%mod;
	printf("%lld\n",ans%mod);
	return 0;
}

 

歐拉函數的另一個主流的應用就是在取模方面,即歐拉定理

$$x^{\varphi(P)}\equiv 1(mod\space P),(gcd(x,P)=1)$$

最經典的題目大概就是[BZOJ3884]上帝與集合的正確用法[BZOJ4869][2017六省聯考]相逢是問候了。

主要思路就是“冪次冪”,利用歐拉函數的性質,證得不斷對於一個數求歐拉函數,即$\varphi(\varphi(...n...))$,只需$2log(n)$次直到1,隨后一直不變。

簡單證明一下,對於大於$1$的整數$n$:

若$n$為奇數,則其中必然含有一個奇質因數,因此$\varphi(n)$為偶數。

若$n$為偶數,則其中必然含有質因數$2$,因此$\varphi(n)\leq \frac n2$。

我也就不寫具體的題解了,還是就把代碼放上吧

[BZOJ3884]上帝與集合的正確用法

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 500200
#define N 10000
using namespace std;
LL read(){
	LL nm=0,fh=1;char cw=getchar();
	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
	return nm*fh;
}
LL n,T,c[M],p[M],pri[M],ph[M],tmp,mod,m,cnt;
bool vis[M];
LL qpow(LL x,LL qs,LL md){
	LL fin=1;
	while(qs){
		if(qs&1) fin=fin*x%md;
		qs>>=1,x=x*x%md;
	}
	return fin;
}
void init(){
	memset(vis,true,sizeof(vis)),ph[1]=1,cnt=0;
	for(LL i=2;i<M;i++){
		if(vis[i]) pri[++cnt]=i,ph[i]=i-1;
		for(LL j=1;pri[j]*i<M&&j<=cnt;j++){
			LL num=pri[j]*i; vis[num]=false;
			if(i%pri[j]==0){ph[num]=ph[i]*pri[j];break;}
			ph[num]=ph[i]*ph[pri[j]];
		}
	}
}
LL PHI(LL x){
	LL fin=x,rem=x; 
	if(x<M) return ph[x];
	for(LL j=1;j<=cnt&&pri[j]<=rem;j++){
		if(rem%pri[j]==0) fin/=pri[j],fin*=(pri[j]-1);
		while(rem%pri[j]==0) rem/=pri[j];
	}
	if(rem>1) fin/=rem,fin*=(rem-1);
	return fin; 
}
LL calc(){
	for(p[tmp=0]=mod;p[tmp]>1;tmp++) p[tmp+1]=PHI(p[tmp]);
	p[++tmp]=1;	LL res=2;
	for(LL i=tmp;i;i--){
		res%=p[i],res+=p[i];
		res=qpow(2,res,p[i-1]);
	}
	return res;
}
int main(){
	init(),T=read();
	while(T--) mod=read(),printf("%lld\n",calc());
	return 0;
}

[BZOJ4869][2017六省聯考]相逢是問候

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 500200
#define N 10000
using namespace std;
LL read(){
	LL nm=0,fh=1;char cw=getchar();
	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
	return nm*fh;
}
LL n,T,c[M],p[M],f[M],tpe,L,R,tg[M],pre[M];
LL cnt,tk[M],P[1000],isp[M],pri[M],ph[M],Mi[10010][70][2],tmp,mod,m;
bool vis[M];
LL find(LL x){return f[x]==x?x:f[x]=find(f[x]);}
LL qpow(LL x,LL qs,LL md){
	LL fin=1;
	while(qs){
		if(qs&1) fin=fin*x%md;
		qs>>=1,x=x*x%md;
	}
	return fin;
}
LL mul(LL kd,LL tim){return Mi[tim%N][kd][0]*Mi[tim/N][kd][1]%P[kd];}
void add(LL pos,LL x){for(LL k=pos;k<=n;k+=(k&(-k))) (c[k]+=x)%=mod;}
LL sum(LL pos){
	LL tot=0ll;
	for(LL k=pos;k>0;k-=(k&-k)) (tot+=c[k]+mod)%=mod;
	return tot;
}
void init(){
	memset(vis,true,sizeof(vis)),ph[1]=1,cnt=0;
	for(LL i=2;i<M;i++){
		if(vis[i]) pri[++cnt]=i,ph[i]=i-1;
		for(LL j=1;pri[j]*i<M&&j<=cnt;j++){
			LL num=pri[j]*i; vis[num]=false;
			if(i%pri[j]==0){ph[num]=ph[i]*pri[j];break;}
			ph[num]=ph[i]*ph[pri[j]];
		}
	}
}
LL PHI(LL x){
	LL fin=x,rem=x;
	if(x<M){return ph[x];}
	for(LL j=1;j<=cnt&&pri[j]<=rem;j++){
		if(rem%pri[j]==0) fin/=pri[j],fin*=(pri[j]-1);
		while(rem%pri[j]==0) rem/=pri[j];
	}
	if(rem>1) fin/=rem,fin*=(rem-1);
	return fin; 
}
LL calc(LL kd,LL now){ 
    for(LL i=kd;i;i--){
        if(now>=P[i]) now=now%P[i]+P[i]; 
		now=mul(i-1,now);
        if(!now) now=P[i-1];
    }
    return now;
}
int main(){
	n=read(),T=read(),mod=read(),m=read();
	for(LL i=1;i<=n;i++) f[i]=i,p[i]=read(),pre[i]=p[i],add(i,p[i]);
	P[0]=mod,init(),f[n+1]=n+1;
	for(tmp=0;P[tmp]>1;tmp++) P[tmp+1]=PHI(P[tmp]);
	P[++tmp]=1;
	for(LL kd=0;kd<=tmp;kd++){
		Mi[0][kd][0]=Mi[0][kd][1]=1%P[kd];
		for(LL i=1;i<=N;i++) Mi[i][kd][0]=Mi[i-1][kd][0]*m%P[kd];
		for(LL i=1;i<=N;i++) Mi[i][kd][1]=Mi[i-1][kd][1]*Mi[N][kd][0]%P[kd];
	}
	while(T--){
		tpe=read(),L=read(),R=read();
		if(tpe){printf("%lld\n",(sum(R)-sum(L-1)+mod)%mod);continue;}
		for(LL now=find(L);now<=R;now=find(now+1)){
			tg[now]++;LL num=calc(tg[now],p[now]),dt;
			dt=num+mod-pre[now],add(now,dt%mod);
			if(tg[now]==tmp) f[now]=f[find(now+1)];
			pre[now]=num;
		}
	}
	return 0;
}

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM