数论-欧拉函数
数论-欧拉函数
继整除分块后的下一篇数论恶补……
参考资料
https://blog.csdn.net/weixin_43237242/article/details/97388834
跳转按钮
\(\texttt{讲解证明}\)
欧拉函数就是 \(\varphi(n)\),表示小于 \(n\) 的正整数中与 \(n\) 互质的数的个数。
把 \(n\) 质因数分解,设
因为 \(x<n\) 只要取到一个 \(p_i\mid x\) 那么 \(x\) 就不与 \(n\) 互质了,所以 \(p_i\) 个数中只能取那不被 \(p_i\) 整除的 \(p_i-1\) 个 \(x\)。所以
这时求一个 \(\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)\)。
然后可以 \(\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;
}
然后就结束了,我数论太蒻了。祝大家学习愉快!