洛谷 P2158 [SDOI2008]仪仗队 欧拉函数

题目链接: 点我跳转
题目大意:如下图,给出一个方阵,求角落里的人可以直接看到的人的数量
洛谷 P2158 [SDOI2008]仪仗队 欧拉函数_第1张图片
题目分析:

1.暴力算法

如果(x,y)位置的人可以被看到,那么(kx,ky)位置的人一定可以被看到,那么我们不难发现,当且仅当,x与y互质的时候可以被看到,否则,会被 ( x m , y m ) (\frac xm,\frac ym) (mx,my)挡住,(m是最大公约数),显然,我们可以发现,最为暴力的思路就是枚举(x,y),然后计算 g c d ( x , y ) gcd(x,y) gcd(x,y)是否为1来判断x与y是否互质。但是,,,显然这种算法的复杂度太高了。我们需要一种更为快捷的方法来计算互质。

2.欧拉函数

接下来是这道题的重点,欧拉函数是指:所有小于n的,并且和n互质的数的个数。
关于欧拉函数的推导过程这里就不细说了(有兴趣自行百度吧,挺有趣的)
下面给出欧拉函数的公式:
φ ( n ) = n ∗ ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋯ ( 1 − 1 p k ) φ(n)=n*(1-\frac 1{p_1})(1-\frac 1{p_2})\cdots(1-\frac 1{p_k}) φ(n)=n(1p11)(1p21)(1pk1)
这个公式一定要记下这个公式一定要记下这个公式一定要记下
其中 p i p_i pi是n的质因数,并且是不重复的例如 φ ( 12 ) = 12 ∗ ( 1 − 1 2 ) ∗ ( 1 − 1 3 ) = 4 φ(12)=12*(1-\frac 12)*(1-\frac 13)=4 φ(12)=12(121)(131)=4表示12有4个小于12互质的数1,5,7,11。
有了欧拉函数这等神器我们就可以轻松解决这道题了

3.筛法欧拉函数

顾名思义,用来统计一段区间上面的许多数的欧拉函数,要不然每个数都要做一次质因数分解。
还是利用了筛的思想,我们首先令所有数的欧拉值都是自己,对于质数p,欧拉值是p-1,(除了自己剩下的都和他互质),然后将所有以p为质因数的合数都乘以 ( 1 − 1 p ) = ( p − 1 ) / p (1-\frac 1p) = (p-1)/p (1p1)=(p1)/p,这样就可以高效地计算区间上的欧拉函数了

int euler[40000+5];
void getEuler(int n){
    memset(euler,0,sizeof(euler));
    euler[1] = 1;
    for(int i=2;i<=n;i++)
        if(!euler[i])                   //euler[i] = 0说明是一个质数
        for(int j=i;j<=n;j+=i){
            if(!euler[j])euler[j] = j;  //euler[j] = 0只是说明还没赋值
            euler[j] = euler[j] / i * (i-1);
        }
}

下面是这道题的完整代码

#include
#include
#include
#include
#include

using namespace std;

int euler[40000+5];

void getEuler(int n){
    memset(euler,0,sizeof(euler));
    euler[1] = 1;
    for(int i=2;i<=n;i++)
        if(!euler[i])
        for(int j=i;j<=n;j+=i){
            if(!euler[j])euler[j] = j;
            euler[j] = euler[j] / i * (i-1);
        }
}

int solve(int n){
    int ans = 0;
    for(int i=1;i<=n;i++){
        ans += euler[i];
    }
    return ans*2+1;
}

int main(){
    int n;
    cin>>n;
    if(n == 1){cout<<0;return 0;}
    getEuler(n-1);
    cout<<solve(n-1);
    return 0;
}

你可能感兴趣的:(洛谷,数论)