定义:欧拉函数是指小于x的数中,与x互质的数的个数。

特别的,我们知道1的欧拉函数值为0,素数p的欧拉函数值为p-1,当数x不是p素数时,由唯一分解定理我们知道x=p1a1*p2a2*p3a3*……*pnan,其中p1,p2……pn皆为素数,则:phi[x]=x*(p1-1)/p1*(p2-1)/p2……*(pn-1)/pn,证明略。由此我们可以得到一个非线性的求欧拉函数的算法。

void euler() {
   phi[1]=0;
    for(int i=2;i<maxn;i++)
    {
        if(!phi[i])//素数 
            for(int j=i;j<maxn;j+=i)//素数去筛它所有的倍数 
	   {
                if(!phi[j])phi[j]=j;//初始值 
                phi[j]=phi[j]/i*(i-1);//公式 
            }
    }
}

  其实欧拉函数还有线性的求法,这里我们需要知道:

1:phi[pk]=pk-pk-1,因为与pk不互质的数有p,2p,3p……(pk-1-1)*p,所以phi[pk]=pk-1-(pk-1-1)=pk-pk-1

2:如果i是p的倍数,那么phi[i*p]=p&phi[i],否则phi[i*p]=(p-1)*phi[i].

这样我们就可以在进行素数筛的时候顺便把欧拉函数求出来。

void getphi(int n)
{
	vis[0]=vis[1]=1,phi[1]=0;
	for(int i=2;i<=n;++i)
	{
		if(!vis[i])
		{
			pri[++cnt]=i,phi[i]=i-1;
		}
		for(int j=1;j<=cnt&&pri[j]*i<=n;++j)
		{
			vis[i*pri[j]]=1;
			if(i%pri[j]==0)
			{
				phi[i*pri[j]]=pri[j]*phi[i];
				break;
			}else
			phi[i*pri[j]]=(pri[j]-1)*phi[i];
		}
	} 
}

  当然,如果我们只需要求一个数的欧拉函数值,就不用这么麻烦了,直接枚举它的因子,按照公式计算就可以了。

for(int i=2;i<=n;++i)
	{
		if(n%i==0)ans=ans/i*(i-1);//因为是从小到大枚举的因子,所以一定都是素因子,这个复杂度比线性的未必小多少,
//但是如果不需要打表的时候写着省事,而且节省空间 while(n%i==0)n/=i; }