欧拉Euler函数

文章目录

  • 概述
      • 概念
      • 思想
      • 朴素定义式
      • 性质
  • 程序详解
      • 朴素暴力
      • 埃氏筛欧拉值
      • 欧拉筛欧拉值
  • 例题
      • 仪仗队
          • 暴力计算代码
          • 埃氏筛欧拉值代码
          • 欧拉筛欧拉值

概述

概念

欧拉函数是求小于等于某个数num的所有数中,与num互质的个数

思想

其实就是利用了分解质因数
首先我们举个例子
求小于12的数中与12互质的数(也就是12的欧拉值)
首先我们设置12的欧拉值为12本身(1 ~ 12都算进去)
1.12的质因数中有2,所以所有2的倍数都不能与12互质,所以12中要筛去2的倍数也就是12 / 2 = 6个,我们将值缩小为12 * (2 - 1) / 2 = 6
2.12的质因数有3,所以3的倍数都不能与12互质,所以12中要筛去3的倍数也就是12 / 3 = 4个,所以再将值缩小为6 * (3 - 1) / 3 = 4
然后就没有了,所以12的欧拉值为4,有4个12以内的数能跟12互质(也就是1、5、7、11)

朴素定义式

我们设num的欧拉值为phi[num]
在num所有的质因子中,对于每个质因子x,都能使num的欧拉值筛去num / x个,会变成num * (x - 1) / x个

性质

质数num的欧拉值 = num - 1
欧拉函数为积性函数,即当gcd(x, y) == 1时,phi(x * y) = phi(x) * phi(y)
且n为奇质数时,phi(2 * n) = phi(n)
对于x % y == 0,有phi(x * y) = phi(x) * y

程序详解

朴素暴力

我们可以暴力分解num的质因数,每次对枚举到的质因数x,使phi[num] * (x - 1) / x

inline int PHI(int num){
     
        int res = num;
        for(int i = 2; i * i <= n; i ++){
     
                if(num % i == 0){
     //美剧到了质因子
                        res = res * (i - 1) / i;
                        while(num % i == 0) num /= i;//抹掉当前质因子
                }
        }
        if(num > 1) res = res * (num - 1) / num;//抹掉可能出现的剩的一个质因子
        return res;
}

此方法在建立1 ~ n的欧拉值时所用时间逼近O(n^2),所以我们需要更快的方法

在学习下面两个之前,如果不懂埃氏筛与欧拉筛的可以点击此处--------传送门

埃氏筛欧拉值

利用了埃氏筛的思想
枚举1 ~ n,如果枚举到的是质数(没被筛过)
每个质数的倍数的欧拉值都可以被当前质数作为质因子筛一遍

const int maxn;
int phi[maxn];//欧拉值
inline void GET_PHI(){
     
        for(int i = 0; i <= maxn; i ++) phi[i] = i;//初始化为本身,因为每个质数的第一次筛都可以变成当前数-1
        for(int i = 2; i <= maxn; i ++){
     
                if(phi[i] == i){
     //没有被筛过
                        for(int j = i; j <= maxn; j += i) 
                                phi[j] = phi[j] * (i - 1) / i;//当前j是以i作为质因子的数
                }
        }
}

欧拉筛欧拉值

利用了欧拉筛的思想
避免重复筛的情况
节省了时间

const int maxn;
bool isprime[maxn];//isprime[i]表示i是否为质数
int prime[maxn];//prime[i]表示第i个质数的值
int phi[maxn];//欧拉值
int cnt = 0;
inline void GET_PHI(){
     
        phi[1] = 1;
        for(int i = 2; i <= maxn; i ++){
     
                if(!isprime[i]) prime[++cnt] = i, phi[i] = i - 1;
                for(int j = 1; j <= cnt && i * prime[j] <= maxn; j ++){
     
                        isprime[i * prime[j]] = 1;
                        if(i % prime[j] == 0){
     
                                phi[i * prime[j]] = phi[i] * prime[j];//定理:对于i % j == 0,有phi[i * j] = phi[i] * j
                                break;
                        }else phi[i * prime[j]] = phi[i] * (prime[j] - 1);//积性函数:如果gcd(i, j) == 1, phi[i * j] = phi[i] * phi[j]。在这里j为质数,所以有phi[i] * phi[j] = phi[i] * (j - 1)
                } 
        }
}

例题

仪仗队

洛谷《仪仗队》传送门
欧拉Euler函数_第1张图片
思路:
这道题我们可以将一个正方形沿对角线拆分成两部分
然后每一个能被看到的点一定是gcd(x, y) = 1的点
对每一行进行分析
在对角线内,gcd = 1的点也就是当前行数的欧拉值
所以我们在不计算(1, 1)的情况下
在一个三角形内,累加行数的欧拉值
再乘二得到两个三角形的欧拉值,再加上(1, 1)这个点即可

暴力计算代码
/*
           ________   _                                              ________                              _
          /  ______| | |                                            |   __   |                            | |
         /  /        | |                                            |  |__|  |                            | |
         |  |        | |___    _   _   _   ___  _   _____           |     ___|   ______   _____   ___  _  | |
         |  |        |  __ \  |_| | | | | |  _\| | | ____|          |  |\  \    |  __  | |  _  | |  _\| | | |
         |  |        | |  \ |  _  | | | | | | \  | | \___           |  | \  \   | |_/ _| | |_| | | | \  | | |
         \  \______  | |  | | | | \ |_| / | |_/  |  ___/ |          |  |  \  \  |    /_   \__  | | |_/  | | |
Author :  \________| |_|  |_| |_|  \___/  |___/|_| |_____| _________|__|   \__\ |______|     | | |___/|_| |_|
                                                                                         ____| |
                                                                                         \_____/
*/
//#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define G 10.0
#define LNF 1e18
#define EPS 1e-6
#define PI acos(-1.0)
#define INF 0x7FFFFFFF

#define ll long long
#define ull unsigned long long

#define LOWBIT(x) ((x) & (-x))
#define LOWBD(a, x) lower_bound(a.begin(), a.end(), x) - a.begin()
#define UPPBD(a, x) upper_bound(a.begin(), a.end(), x) - a.begin()
#define TEST(a) cout << "---------" << a << "---------" << '\n'

#define CHIVAS int main()
#define _REGAL exit(0)

#define SP system("pause")
#define IOS ios::sync_with_stdio(false)
//#define map unordered_map

#define PB(x) push_back(x)
#define ALL(a) ((a).begin(),(a).end())
#define MEM(a, b) memset(a, b, sizeof(a))
#define EACH_CASE(cass) for (scanf("%d", &cass); cass; cass--)

#define LS l, mid, rt << 1
#define RS mid + 1, r, rt << 1 | 1
#define GETMID (l + r) >> 1

using namespace std;

template<typename T> inline T MAX(T a, T b){
     return a > b? a : b;}
template<typename T> inline T MIN(T a, T b){
     return a > b? b : a;}
template<typename T> inline void SWAP(T &a, T &b){
     T tp = a; a = b; b = tp;}
template<typename T> inline T GCD(T a, T b){
     return b > 0? gcd(b, a % b) : a;}
template<typename T> inline void ADD_TO_VEC_int(T &n, vector<T> &vec){
     vec.clear(); cin >> n; for(int i = 0; i < n; i ++){
     T x; cin >> x, vec.PB(x);}}
template<typename T> inline pair<T, T> MaxInVector_ll(vector<T> vec){
     T MaxVal = -LNF, MaxId = 0;for(int i = 0; i < (int)vec.size(); i ++) if(MaxVal < vec[i]) MaxVal = vec[i], MaxId = i; return {
     MaxVal, MaxId};}
template<typename T> inline pair<T, T> MinInVector_ll(vector<T> vec){
     T MinVal = LNF, MinId = 0;for(int i = 0; i < (int)vec.size(); i ++) if(MinVal > vec[i]) MinVal = vec[i], MinId = i; return {
     MinVal, MinId};}
template<typename T> inline pair<T, T> MaxInVector_int(vector<T> vec){
     T MaxVal = -INF, MaxId = 0;for(int i = 0; i < (int)vec.size(); i ++) if(MaxVal < vec[i]) MaxVal = vec[i], MaxId = i; return {
     MaxVal, MaxId};}
template<typename T> inline pair<T, T> MinInVector_int(vector<T> vec){
     T MinVal = INF, MinId = 0;for(int i = 0; i < (int)vec.size(); i ++) if(MinVal > vec[i]) MinVal = vec[i], MinId = i; return {
     MinVal, MinId};}
//template inline pair, vector > DIV(T n){T nn = n;map cnt;vector div;for(ll i = 2; i * i <= nn; i ++){while(n % i == 0){if(!cnt[i]) div.push_back(i);cnt[i] ++;n /= i;}}if(n != 1){if(!cnt[n]) div.push_back(n);cnt[n] ++;n /= n;}return {cnt, div};}

inline int PHI(int n){
     
    int res = n;
    for(int i = 2; i * i <= n; i ++){
     
        if(n % i == 0){
     
            res = res * (i - 1) / i;
            while(n % i == 0) n /= i;
        }
    }
    if(n > 1) res = res * (n - 1) / n;
    return res;
}

CHIVAS{
     
    int n;
    cin >> n;
    if(n == 1){
     
        cout << 0 << endl;
        return 0;
    }
    int res = 0;
    for(int i = 2; i < n; i ++) res += PHI(i);
    cout << res * 2 + 3 << endl;
    _REGAL;
}
埃氏筛欧拉值代码
/*
           ________   _                                              ________                              _
          /  ______| | |                                            |   __   |                            | |
         /  /        | |                                            |  |__|  |                            | |
         |  |        | |___    _   _   _   ___  _   _____           |     ___|   ______   _____   ___  _  | |
         |  |        |  __ \  |_| | | | | |  _\| | | ____|          |  |\  \    |  __  | |  _  | |  _\| | | |
         |  |        | |  \ |  _  | | | | | | \  | | \___           |  | \  \   | |_/ _| | |_| | | | \  | | |
         \  \______  | |  | | | | \ |_| / | |_/  |  ___/ |          |  |  \  \  |    /_   \__  | | |_/  | | |
Author :  \________| |_|  |_| |_|  \___/  |___/|_| |_____| _________|__|   \__\ |______|     | | |___/|_| |_|
                                                                                         ____| |
                                                                                         \_____/
*/
//#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define G 10.0
#define LNF 1e18
#define EPS 1e-6
#define PI acos(-1.0)
#define INF 0x7FFFFFFF

#define ll long long
#define ull unsigned long long

#define LOWBIT(x) ((x) & (-x))
#define LOWBD(a, x) lower_bound(a.begin(), a.end(), x) - a.begin()
#define UPPBD(a, x) upper_bound(a.begin(), a.end(), x) - a.begin()
#define TEST(a) cout << "---------" << a << "---------" << '\n'

#define CHIVAS int main()
#define _REGAL exit(0)

#define SP system("pause")
#define IOS ios::sync_with_stdio(false)
//#define map unordered_map

#define PB(x) push_back(x)
#define ALL(a) ((a).begin(),(a).end())
#define MEM(a, b) memset(a, b, sizeof(a))
#define EACH_CASE(cass) for (scanf("%d", &cass); cass; cass--)

#define LS l, mid, rt << 1
#define RS mid + 1, r, rt << 1 | 1
#define GETMID (l + r) >> 1

using namespace std;

template<typename T> inline T MAX(T a, T b){
     return a > b? a : b;}
template<typename T> inline T MIN(T a, T b){
     return a > b? b : a;}
template<typename T> inline void SWAP(T &a, T &b){
     T tp = a; a = b; b = tp;}
template<typename T> inline T GCD(T a, T b){
     return b > 0? gcd(b, a % b) : a;}
template<typename T> inline void ADD_TO_VEC_int(T &n, vector<T> &vec){
     vec.clear(); cin >> n; for(int i = 0; i < n; i ++){
     T x; cin >> x, vec.PB(x);}}
template<typename T> inline pair<T, T> MaxInVector_ll(vector<T> vec){
     T MaxVal = -LNF, MaxId = 0;for(int i = 0; i < (int)vec.size(); i ++) if(MaxVal < vec[i]) MaxVal = vec[i], MaxId = i; return {
     MaxVal, MaxId};}
template<typename T> inline pair<T, T> MinInVector_ll(vector<T> vec){
     T MinVal = LNF, MinId = 0;for(int i = 0; i < (int)vec.size(); i ++) if(MinVal > vec[i]) MinVal = vec[i], MinId = i; return {
     MinVal, MinId};}
template<typename T> inline pair<T, T> MaxInVector_int(vector<T> vec){
     T MaxVal = -INF, MaxId = 0;for(int i = 0; i < (int)vec.size(); i ++) if(MaxVal < vec[i]) MaxVal = vec[i], MaxId = i; return {
     MaxVal, MaxId};}
template<typename T> inline pair<T, T> MinInVector_int(vector<T> vec){
     T MinVal = INF, MinId = 0;for(int i = 0; i < (int)vec.size(); i ++) if(MinVal > vec[i]) MinVal = vec[i], MinId = i; return {
     MinVal, MinId};}
//template inline pair, vector > DIV(T n){T nn = n;map cnt;vector div;for(ll i = 2; i * i <= nn; i ++){while(n % i == 0){if(!cnt[i]) div.push_back(i);cnt[i] ++;n /= i;}}if(n != 1){if(!cnt[n]) div.push_back(n);cnt[n] ++;n /= n;}return {cnt, div};}

const int maxn = 400005;
int phi[maxn];
int n;

inline void GET_PHI(){
     
    for(int i = 0; i <= n; i ++) phi[i] = i;
    for(int i = 2; i <= n; i ++){
     
        if(phi[i] == i){
     
            for(int j = i; j <= n; j += i) phi[j] = phi[j] * (i - 1) / i;
        }
    }
}

CHIVAS{
     
    cin >> n;
    GET_PHI();

    int res = 0;
    for(int i = 1; i < n; i++) res += phi[i];
    cout << (n == 1 ? 0 : (res << 1 | 1)) << endl;
    _REGAL;
}
欧拉筛欧拉值
/*
           ________   _                                              ________                              _
          /  ______| | |                                            |   __   |                            | |
         /  /        | |                                            |  |__|  |                            | |
         |  |        | |___    _   _   _   ___  _   _____           |     ___|   ______   _____   ___  _  | |
         |  |        |  __ \  |_| | | | | |  _\| | | ____|          |  |\  \    |  __  | |  _  | |  _\| | | |
         |  |        | |  \ |  _  | | | | | | \  | | \___           |  | \  \   | |_/ _| | |_| | | | \  | | |
         \  \______  | |  | | | | \ |_| / | |_/  |  ___/ |          |  |  \  \  |    /_   \__  | | |_/  | | |
Author :  \________| |_|  |_| |_|  \___/  |___/|_| |_____| _________|__|   \__\ |______|     | | |___/|_| |_|
                                                                                         ____| |
                                                                                         \_____/
*/
//#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define G 10.0
#define LNF 1e18
#define EPS 1e-6
#define PI acos(-1.0)
#define INF 0x7FFFFFFF

#define ll long long
#define ull unsigned long long

#define LOWBIT(x) ((x) & (-x))
#define LOWBD(a, x) lower_bound(a.begin(), a.end(), x) - a.begin()
#define UPPBD(a, x) upper_bound(a.begin(), a.end(), x) - a.begin()
#define TEST(a) cout << "---------" << a << "---------" << '\n'

#define CHIVAS int main()
#define _REGAL exit(0)

#define SP system("pause")
#define IOS ios::sync_with_stdio(false)
//#define map unordered_map

#define PB(x) push_back(x)
#define ALL(a) ((a).begin(),(a).end())
#define MEM(a, b) memset(a, b, sizeof(a))
#define EACH_CASE(cass) for (scanf("%d", &cass); cass; cass--)

#define LS l, mid, rt << 1
#define RS mid + 1, r, rt << 1 | 1
#define GETMID (l + r) >> 1

using namespace std;

template<typename T> inline T MAX(T a, T b){
     return a > b? a : b;}
template<typename T> inline T MIN(T a, T b){
     return a > b? b : a;}
template<typename T> inline void SWAP(T &a, T &b){
     T tp = a; a = b; b = tp;}
template<typename T> inline T GCD(T a, T b){
     return b > 0? gcd(b, a % b) : a;}
template<typename T> inline void ADD_TO_VEC_int(T &n, vector<T> &vec){
     vec.clear(); cin >> n; for(int i = 0; i < n; i ++){
     T x; cin >> x, vec.PB(x);}}
template<typename T> inline pair<T, T> MaxInVector_ll(vector<T> vec){
     T MaxVal = -LNF, MaxId = 0;for(int i = 0; i < (int)vec.size(); i ++) if(MaxVal < vec[i]) MaxVal = vec[i], MaxId = i; return {
     MaxVal, MaxId};}
template<typename T> inline pair<T, T> MinInVector_ll(vector<T> vec){
     T MinVal = LNF, MinId = 0;for(int i = 0; i < (int)vec.size(); i ++) if(MinVal > vec[i]) MinVal = vec[i], MinId = i; return {
     MinVal, MinId};}
template<typename T> inline pair<T, T> MaxInVector_int(vector<T> vec){
     T MaxVal = -INF, MaxId = 0;for(int i = 0; i < (int)vec.size(); i ++) if(MaxVal < vec[i]) MaxVal = vec[i], MaxId = i; return {
     MaxVal, MaxId};}
template<typename T> inline pair<T, T> MinInVector_int(vector<T> vec){
     T MinVal = INF, MinId = 0;for(int i = 0; i < (int)vec.size(); i ++) if(MinVal > vec[i]) MinVal = vec[i], MinId = i; return {
     MinVal, MinId};}
//template inline pair, vector > DIV(T n){T nn = n;map cnt;vector div;for(ll i = 2; i * i <= nn; i ++){while(n % i == 0){if(!cnt[i]) div.push_back(i);cnt[i] ++;n /= i;}}if(n != 1){if(!cnt[n]) div.push_back(n);cnt[n] ++;n /= n;}return {cnt, div};}

const int maxn = 40010;
bool isprime[maxn];
int prime[maxn];
int phi[maxn];
int cnt = 0;
int n;

inline void GET_PHI(){
     
        phi[1] = 1;
        for(int i = 2; i <= n; i ++){
     
                if(!isprime[i]) prime[++cnt] = i, phi[i] = i - 1;
                for(int j = 1; j <= cnt && i * prime[j] <= n; j ++){
     
                        isprime[i * prime[j]] = 1;
                        if(i % prime[j] == 0){
     
                                phi[i * prime[j]] = phi[i] * prime[j];
                                break;
                        }else phi[i * prime[j]] = phi[i] * (prime[j] - 1);
                }
        }
}
CHIVAS{
     
    cin >> n;
    GET_PHI();
    int res = 0;

    for(int i = 1; i < n; i ++) res += phi[i];
    cout << (n == 1 ? 0 : (res << 1 | 1)) << endl;
    _REGAL;
}

你可能感兴趣的:(#,数论/组合数学,gcd,数学)