你们要的欧拉欧拉欧拉欧拉终于来了
欧拉函数
我们用 φ(n)来表示欧拉函数
它等于<=n的数中与n互质的数的数目(1姑且认为与任何数互质吧)
首先当然是大家都擅长的暴力了,我们知道,两个数互质就是gcd=1,所以就有
int phi(int n)
{
int ans=0;
for(int i=1;i<=n;i++)
if(gcd(i,n)==1)
ans++;
return n;
}
但是很多时候暴力是出不了奇迹的,所以我们需要更好的方法
很多时候我们思考问题要学会逆向思维,只要把不与n互质的数去掉的话剩下的不就是与n互质的了。还记得之前说过的定理吗,在讲线筛的时候,任何合数都能拆成n个素数的乘积。所以只要把小于n的n的所有素因数的倍数去掉就好啦,是不是很简单哇。具体做法就算简单容斥啦(此时,我终于想起了没有做容斥的博客,但是问题不大)
比如说24,它的素因数有2,3
小于等于24&&2的倍数:24/2=12
小于等于24&&3的倍数:24/3=8
2和3的倍数24/(2 * 3)
需要去除的数为24 *(1/2-1/6+1/3)
φ(24)=24(1-1/2-1/3+1/6)=24(1-1/2)(1-1/3)=8
再比如三个素数的2*3*5=30
φ(30)=30(1-1/2)(1-1/3)(1-1/5)=8
至于为什么具体转到容斥定理(还没写)
于是我们就可以有新的写法了
int phi(int n)
{
int ans=n;
for(int i=2;i*i<=n;i++)
{
//由于任何一个合数都至少有一个不大于根号n的素因子,所以只需遍历到根号n即可
if(n%i==0)
{
ans = ans/i*(i-1);
//防溢出
while(n%i==0)n/=i;
//使n的因子没有素数i的倍数
}
}
if(n>1)
ans=ans/n*(n-1);
//如果n最终是素数的话会跳出循环,没有计算,所以这里还要再判断一次
return ans;
}
聪明的同学们可能已经发现了,这东西是可以打表的
遍历一遍,只要遍历到素数的话它的所有倍数(包括它本身)都除以它乘它-1既/i*(i-1)
int phi[N];
void Euler()
{
phi[1]=1;
for(int i=2;i<N;i++)
{
if(phi[i])
{
for(int j=i;j<N;j+=i)
{
if(!phi[j]) phi[j]=j;
phi[j]=phi[j]/i*(i-1);
}
}
}
}
复杂度和埃筛差不多,O(nlnln n)
然后提一下欧拉函数的一些性质
1.若p为质数,φ ( p)=p-1
因为小于质数p的数都与p互质
2.若i mod p = 0,且p为质数,则φ( i * p ) = φ(i) *p
3.若i mod p ≠0,且p为质数, 那么 φ( i * p )=φ(i) * ( p-1 )
4.对于n=p^k ,有
5.若gcd(n,m)=1,
n与m互质,质数互不相同
6. ,则有
p为质数,易得(真的易得吖)
7.若gcd(a,m)=1,则 (mod m)
欧拉定理,下有证明
8.小于n且与n互质的数的和 S=n*;
易知,若i与n互质,则n-i与n也互质
若i与n不互质 gcd(i,n)=a > 0, (n-i)/a=n/a-i/a为整数,故a也为n-i的因数,故 gcd(n,n-i) != 1
所以有S = n*
=n
由性质1 2 3 可得一种更快得筛法
const int N = 1e6+10;
int phi[N],prime[N];
int tot;
void Euler()
{
phi[1] = 1;
for(int i = 2; i < N;i++)
{
if(!phi[i])
{
phi[i]=i-1;
prime[tot++];
}
for(int j=0;j<tot&&1ll*i*prime[j]<N;j++)
{
if(i%prime[j])
phi[i*prime[j]]=phi[i]*(prime[j]-1);
else
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
}
}
}
原理和线筛差不多就不多累赘了
欧拉定理
当a,m互质时,
枚举小于m与m互质的数
令
则对于
$ p_i-p_j=a(x_i-x_j)$
因为a与m互质,与m互质,所以与m互质
既
所以有
因此,对于确定的i,有确定的j使
且i对j是一一映射的
因此得
得证
拓展欧拉定理
证明有缘的话会给出来的(咕咕咕)
看了这个后就不要再写出(mod m)这种东西来了,这个是错的错的错的!
欧拉降幂
就是当指数爆炸(超64位整数,几百位甚至更多)时可以用拓展欧拉定理来处理c mod
int main()
{
ll a,c,p;
char b[100000];
cin>>a>>b>>p;
int l = strlen(b);
for(int i=0;i<l;i++)
c=(c*10+b[i]-'0')%p;
cout<<qpow(a,c%phi(p)+phi(p),p)<<endl;
return 0;
}