Loading

数论-欧拉函数

数论-欧拉函数

继整除分块后的下一篇数论恶补……
参考资料

https://blog.csdn.net/weixin_43237242/article/details/97388834


跳转按钮

\(\texttt{讲解证明}\)


\(\texttt{经典例题}\)


\(\texttt{讲解证明}\)

欧拉函数就是 \(\varphi(n)\),表示小于 \(n\) 的正整数中与 \(n\) 互质的数的个数。

\(n\) 质因数分解,设

\[n=p_1^{x_1}\cdot p_2^{x_2}\cdot...\cdot p_k^{x_k} \]

因为 \(x<n\) 只要取到一个 \(p_i\mid x\) 那么 \(x\) 就不与 \(n\) 互质了,所以 \(p_i\) 个数中只能取那不被 \(p_i\) 整除的 \(p_i-1\)\(x\)。所以

\[\varphi(n)=(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_k})=\prod\limits_{i=1}^k(1-\frac{1}{p_i})=\prod\limits_{i=1}^k\frac{p_i-1}{p_i} \]

这时求一个 \(\varphi(n)\) 的时间复杂度为 \(\Theta(\sqrt n)\)有时候也有用的)。

code

//&Eular
lng Eular(lng n){
	lng ans=n;
	for(lng i=2;i*i<=n;i++)
		if(n%i==0){
			ans=ans/i*(i-1);
			while(n%i==0) n/=i;//把i这个质因子除光
		}
	if(n>1) ans=ans/n*(n-1);
	return ans;
}

很多时候要用到 \(\varphi(1),\varphi(2),...,\varphi(n)\),如果这样一个一个求时间复杂度就是 \(\Theta(n\sqrt n)\)

但是,如果用类似筛质数的方法打表求 \(\varphi(i)\) 时间复杂度就可以到 \(\Theta(\frac n1+\frac n2+...\frac nn)=\Theta(n\log n)\)

code

//&Eular
const int N=1e6+10;
int cnt,phi[N],p[N];
bool np[N];
void Eular(int n){  
	phi[1]=1;
	for(int i=2;i<=n;i++){  
		if(!np[i]) p[++cnt]=i,phi[i]=i-1;
		for(int j=1;j<=cnt&&i*p[j]<=n;j++){  
			np[i*p[j]]=1;  
			if(i%p[j]==0){phi[i*p[j]]=phi[i]*p[j];break;}
			else phi[i*p[j]]=phi[i]*(p[j]-1);
		}  
	}
}

\(\texttt{经典例题}\)

[Cnoi2020]明天后的幻想乡
有一个数列 \(\{1,2,3,...,n\}\), 求它有多少个子序列是等比数列(至少 \(3\) 项,没有重复元素,无序),答案对 \(998244353\) 取模。
数据范围:\(1\le n\le 10^{12}\)


等比数列 \(\{a_1,a_2,...,a_k\}(a_1<a_2<...<a_k,k\ge 3)\)公比不一定是整数,所以设为 \(\frac xy(y<x)\)

\[\because a_k=a_1\cdot (\frac xy)^{k-1} \]

\[\therefore a_ky^{k-1}=a_1x^{k-1} \]

\[x^{k-1}\mid a_k,a_1=a_k\cdot(\frac yx)^{k-1} \]

\[\because k\ge3,a_k\le n,x^{k-1}\mid a_k \]

\[\therefore x=\sqrt[k-1]{a_k}\le\sqrt n \]

然后可以 \(\Theta(\sqrt n)\) 枚举 \(x\),然后再 \(\Theta(\log n)\) 枚举 \(k\),然后就得到了 \(a_k\) 的数量为 \(\lfloor\frac{n}{x^{k-1}}\rfloor\)

得到 \(a_k\) 以后,因为 \(a_1=a_k\cdot(\frac yx)^{k-1}\),而如果 \(x,y\) 不互质的话,就会有重复的等比数列。所以这里的 \(y\) 应该有 \(\varphi(x)\) 种。

然后就都解决了,注意要 \(\bmod 998244353\)


code

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

//&Start
#define lng long long
const int inf=0x3f3f3f3f;
const lng Inf=1e17;

//&Eular
const int N=1e6+10;
int cnt,phi[N],p[N];
bool np[N];
void Eular(int n){  //当时还不知道有前面那种简单的方法
	phi[1]=1;
	for(int i=2;i<=n;i++){  
		if(!np[i]) p[++cnt]=i,phi[i]=i-1;
		for(int j=1;j<=cnt&&i*p[j]<=n;j++){  
			np[i*p[j]]=1;  
			if(i%p[j]==0){phi[i*p[j]]=phi[i]*p[j];break;}
			else phi[i*p[j]]=phi[i]*(p[j]-1);
		}  
	}
}

//&Main
const lng mod=998244353;
lng n,ans;//不开longlong见祖宗
int main(){
	scanf("%lld",&n);
	Eular(sqrt(n));
	for(lng i=2;i*i<=n;i++){//i就是x
		for(lng j=i*i;j<=n;j*=i)//j就是 i^(k-1)
			(ans+=(n/j)%mod*phi[i])%=mod;
	}
	printf("%lld\n",ans);
	return 0;
}

然后就结束了,我数论太蒻了。祝大家学习愉快!

posted @ 2020-03-09 19:07  George1123  阅读(279)  评论(0编辑  收藏  举报