浅谈欧拉函数【复习】

瘦欲@ 提交于 2019-12-03 14:23:09

浅谈欧拉函数【复习】

定义:

φ(n)表示小于n的正整数中和n互质的个数;

性质:

1.积性函数:φ(n×m)=φ(n)×φ(m)(感性理解)

2.a^φ(n)^≡1(mod n),当且仅当gcd(a,n)==1(感性理解)

3.[1,n]中与n互质的数的和为n×φ(n)/2

4.Σφ(d)=n,其中(d|n)(感性理解)

5.φ(p^a^)=p^a^-p^a-1^,其中(p为素数,a为正整数)

证明:

这里插入个游戏:

问题:求正整数3^83^的最后两位数

回到正题

一:√n求单个数的欧拉函数值:

根据性质五:

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1e7 + 10;
int p, ans = 1, N;
void GetPhi() {
    for(int i = 2; i * i <= p; i++) {
        if(p % i == 0) {
            int now = i - 1; p /= i;
            while(p % i == 0) now = now * i, p /= i;
            ans = ans * now;
        }
    }
    if(p != 1) ans *= (p - 1); 
}
int main() {
    cin >> p; N = p;
    GetPhi();
    cout << ans;
    return 0;
}

二:线性塞欧拉函数

以下就是用到的性质

性质一:

φ(p)=p-1,当且仅当p为素数时

性质二:

若p不为i的约数且p为素数

则φ(i∗p)=φ(i)∗φ(p)

=φ(i∗p)=φ(i)∗(p−1)

这一步同时利用了性质1和欧拉函数的积性

性质3

若p是i的约数且p为素数

则φ(i∗p)=φ(i)∗p

这个根据最开始的性质五可以得到

part code by wzxbeliever:

void init(){
    phi[1]=1;
    for(ri i=2;i<=n;i++){
        if(!vis[i])prime[++tot]=i,phi[i]=i-1;
        for(ri j=1;j<=tot&&i*prime[j]<=n;j++){
            vis[i*prime[j]]=1;
            if(!(i%prime[j])){phi[i*prime[j]]=phi[i]*prime[j];break;}
            else phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
    }
}
光说不用等于放屁

例题一:

https://www.luogu.org/problem/P2158

分析:

由图观察发现能被看到的点当且仅当gcd(i,j)==1,

注意看图的时候将两点间空格看成一格,不要将点看成一格

所以问题转化为互质的点对有多少个

这里很容易想到欧拉函数

因为两边是对称的,只算一边就好了

code by wzxbeliever:

#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ri register int
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=40005;
int n,tot;
ll ans;
bool vis[maxn];
int phi[maxn],prime[maxn];
il void init(){
    phi[1]=1; 
    for(ri i=2;i<=n;i++){
        if(!vis[i])prime[++tot]=i,phi[i]=i-1;
        for(ri j=1;j<=tot&&i*prime[j]<=n;j++){
            vis[i*prime[j]]=1;
            if(!(i%prime[j])){phi[i*prime[j]]=phi[i]*prime[j];break;}
            else phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
    }
}
int main(){
    scanf("%d",&n);
    if(n==1){printf("0\n");return 0;}//注意特判0
    init();
    for(ri i=1;i<=n-1;i++)
    ans+=phi[i];
    ans<<=1;ans++;
    printf("%lld\n",ans);
    return 0;
}

大体上就这么多了

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!