关于欧拉函数Phi&&BZOJ2190

欧拉函数(φ)phi是OI中很重要的一块知识点,经常出现在各种数论题目里,所以这里就讲一下phi的求法以及最简单的应用。
φ(n)的含义是在1~n中与n互质的数的个数。那么这里有一个十分明显的结论,就是如果x为质数,则φ(x)=x-1,因为1~x-1都与x互质,都不是x的因数。下面我将介绍更多的关于phi的结论。
结论1:若x为质数,φ(x)=x-1(如上文)
结论2:若p为质数,k>1,则φ(p^k)=p^k-p^(k-1)=(p^k)*(1-1/p)。这个结论也十分容易证明,因为如果这个数为p^k,则与它不互质的数只必须有因数p,而包含p的是1×p,2×p……(p^(k-1))×p共p^(k-1)个。
结论3:phi是积性函数,所以若x和y互质,则φ(x*y)=φ(x)*φ(y)
结论4:(由结论2扩展可知),对于任意大于1的数,将其分解质因数得x=∏pi^ki。所以φ(x)=x*∏(1-1/pi)。
我们利用结论3,就可以进行线性筛欧拉函数,我们在进行欧拉筛的时候顺便进行欧拉函数的筛选,我们先看最普通的欧拉筛代码。
pri[0]=pri[1]=1;pri[2]=0;
for(int i=2;i<=n;i++){
    if(!pri[i]) b[++top]=i;
    for(int j=1;j<=top;j++){
        if(i*b[j]>n) break;
        if(i%b[j]){pri[i*b[j]]=1;}
        if(i%b[j]==0){pri[i*b[j]]=1;break;}
    }
}
我们发现(i%b[j])的情况也就是i和b[j]互质的情况,所以这时直接phi[i*b[j]]=phi[i]*phi[b[j]]即可。
那么对于(i%b[j]==0)的情况呢?这时phi[i*b[j]]=phi[i]*b[j],这里证明就不展开了
pri[0]=1;pri[1]=1;pri[2]=0;phi[1]=1;
    for(int i=2;i<=n;i++){
        if(!pri[i]){
            b[++top]=i;phi[i]=i-1;
        }
        for(int j=1;j<=top;j++){
            if(i*b[j]>n) break;
            pri[i*b[j]]=1;
            if(i%b[j]) phi[i*b[j]]=phi[i]*phi[b[j]];
            else{
                phi[i*b[j]]=phi[i]*b[j];break;
            }
        }
    }
这一段就是求phi的过程,可知我们就是在欧拉筛的过程中加上了求phi的语句。
有些时候不需要线性求phi,则我们直接用结论4即可。
#include<bits/stdc++.h>
#define MAXN 40006
using namespace std;
int read(){
    char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,m,b[MAXN],pri[MAXN],phi[MAXN],top;
int main()
{
    n=read(),m=read();
    pri[0]=1;pri[1]=1;pri[2]=0;phi[1]=1;
    for(int i=2;i<=n;i++){
        if(!pri[i]){
            b[++top]=i;phi[i]=i-1;
        }
        for(int j=1;j<=top;j++){
            if(i*b[j]>n) break;
            pri[i*b[j]]=1;
            if(i%b[j]) phi[i*b[j]]=phi[i]*phi[b[j]];
            else{
                phi[i*b[j]]=phi[i]*b[j];break;
            }
        }
    }
    for(int i=1;i<=m;i++){
        int x=read();printf("%d\n",phi[x]);
    }
    return 0;
}
一道简单的求phi题,BZOJ2190

Description
作为体育委员,C君负责这次运动会仪仗队的训练。仪仗队是由学生组成的N * N的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。
这里写图片描述
现在,C君希望你告诉他队伍整齐时能看到的学生人数。
Input
  共一个数N。
Output
  共一个数,即C君应看到的学生人数。
Sample Input
  4
Sample Output
  9
Hint
【数据规模和约定】  对于 100% 的数据,1 ≤ N ≤ 40000

这题很明显,我们先去掉最左边的一行和最下面的一行,将答案加2,然后剩下的点(其横纵坐标均-1),如果其x和y互质,则这个点时可以被看到的,因为很明显若x=bq,y=bp(b>1),则(q,p)会挡住(x,y)。所以我们直接累加phi值即可。

#include<bits/stdc++.h>
#define ll long long
#define MAXN 40006
using namespace std;
ll read(){
    char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
ll n,m,b[MAXN],pri[MAXN],phi[MAXN],top,ans;
int main()
{
    n=read();
    if(n==1){puts("0");return 0;}
    pri[0]=1;pri[1]=1;pri[2]=0;phi[1]=1;
    for(ll i=2;i<=n;i++){
        if(!pri[i]){
            b[++top]=i;phi[i]=i-1;
        }
        for(ll j=1;j<=top;j++){
            if(i*b[j]>n) break;
            pri[i*b[j]]=1;
            if(i%b[j]) phi[i*b[j]]=phi[i]*phi[b[j]];
            else{
                phi[i*b[j]]=phi[i]*b[j];break;
            }
        }
    }
    ans=2;
    for(ll i=1;i<=n-1;i++) ans+=phi[i]*2;
    printf("%lld",ans-1);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/stevensonson/article/details/79959976