欧拉函数

欧拉函数

[AcWing873] 欧拉函数

873. 欧拉函数 - AcWing题库

题目描述

给定 \(n\) 个正整数 \(a_i\),请你求出每个数的欧拉函数。

欧拉函数的定义

\(1 \sim N\) 中与 \(N\) 互质的数的个数被称为欧拉函数,记为 \(ϕ(N)\)
若在算数基本定理中,\(N = p_1^{a_1}p_2^{a_2}…p_m^{a_m}\),则:
\(ϕ(N)\) = \(N \times \frac{p_1-1}{p_1} \times \frac{p_2-1}{p_2} \times … \times \frac{p_m-1}{p_m}\)

输入格式

第一行包含整数 \(n\)

接下来 \(n\) 行,每行包含一个正整数 \(a_i\)

输出格式

输出共 \(n\) 行,每行输出一个正整数 \(a_i\) 的欧拉函数。

数据范围

\(1 \le n \le 100\),
\(1 \le a_i \le 2 \times 10^9\)

输入样例:

3
3
6
8

输出样例:

2
2
4

算法

这里给出欧拉函数的部分知识。

欧拉函数的定义

$1\sim n $ 中与 \(n\) 互质的数的个数成为欧拉函数,记为 \(ϕ(n)\)

例:\(\phi(1)=1,\phi(2)=1,\phi(3)=2,\phi(4)=2,\phi(5)=4\)

欧拉函数的性质

  1. \(p\) 是质数,则 \(ϕ(p)=p-1\)
  2. \(p\) 是质数,则 \(\phi(p^k)=(p-1)p^{k-1}\)
  3. 积性函数:若 \(gcd(m,n)=1\),则 \(\phi(mn)=\phi(m)\phi(n)\)

欧拉函数是一个积性函数

欧拉函数的计算公式

由唯一分解定理,对于一个正整数 \(n(n>1)\) 可以分解质因数,得到

\[n=\prod_{i=1}^{k}p_{i}^{a_i}=p_{1}^{a_1}\cdot p_{2}^{a_2}\cdots p_{k}^{a_k} \]

\[\begin{align} \phi(n)&=\prod_{i=1}^{k}p_{i}^{a_i}\\ &= \prod_{i=1}^{k}(p_i-1)p_{i}^{a_i-1}\quad(性质2)\\ &=\prod_{i=1}^{k}p_{i}^{a_i}(1-\frac{1}{p_i} )\\ &=\prod_{i=1}^{k}p_{i}^{a_i}\times \prod_{i=1}^{k}(1-\frac{1}{p_i} )\\ &=n\times \prod_{i=1}^{k}\frac{p_i-1}{p_i} \\ &=n\times \frac{p_1-1}{p_1}\times \frac{p_2-1}{p_2}\times\cdots \frac{p_k-1}{p_k} \end{align} \]

欧拉函数只与 \(n\) 及其质因子 \(p_i\) 决定,与质因子次数 \(a_i\) 无关。

对于 \(p\) 为质数的情况,我们有 \(\phi(p)=p\times (1-\frac{1}{p})=p-1\) ,性质1得证。

以下代码就是基于 \(\phi(n)\) 的表达式编写。

C++ 代码

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int T; cin >> T;
    while (T--){
        int n; cin >> n;
        // 注意这里要先乘一个n
        auto ans = n * 1ll;
        unordered_map<int,int> hash;
        for (int i = 2; i <= n / i; i++){
            while (n % i == 0){
                n /= i;
                hash[i] ++;// 次数更新
            }
            // 把质因子i给除尽了
        }
        if (n > 1) hash[n] ++;
        for (auto i : hash){
            int p = i.first;
            ans = ans * (p - 1) / p;// 每次乘(p-1)/p
        }
        cout << ans << endl;
    }

    return 0;
}


[AcWing874] 筛法求欧拉函数

874. 筛法求欧拉函数 - AcWing题库

题目描述

给定一个正整数 \(n\),求 \(1 \sim n\) 中每个数的欧拉函数之和。

输入格式

共一行,包含一个整数 \(n\)

输出格式

共一行,包含一个整数,表示 \(1 \sim n\) 中每个数的欧拉函数之和。

数据范围

\(1 \le n \le 10^6\)

输入样例:

6

输出样例:

12

算法

这里利用了线性筛(欧拉筛)的方法。

具体方法可见质数的线性筛知识。

此处的筛是指利用数组 \(vis\) 判断有无遍历

第一层循环:\(i=2\sim n\) ,判断 \(i\) 是否为 \(n\) 的因子。

如果 \(i\) 没有遍历过,说明一定是一个最小质因子,\(\phi(i)=i-1\) (性质1),并加入质数数组 \(prime\)

第二层循环:\(j=0\sim (i\times prime[j]\leq n)\) ,遍历质数数组 \(prime\)

对于合数 \(m=i\times prime[j]\) ,先用 \(vis\) 筛掉 \(m\) ,并讨论两种情况:

  1. 如果 \(i\ \%\ prime[j]==0\),即 \(i\)\(prime[j]\) 不互质,即 \(gcd(i,prime[j])\ne 1\)不可以用性质3\(i\) 本身就是个合数。

    那么 \(i\) 就包含了 \(m\) 的所有质因子,即 \(i\)\(m\) 的质因子一样

    所以 \(\phi(m)=prime[j]\times \phi(i)\)

    此处加以证明:

    \(m=i\times p\) ,其中 \(p\) 为质数。

    \(p\)\(i\) 的因子,则对于 \(i=\prod_{j=1}^{k}p_{j}^{a_j}\) ,必定存在 \(p_t\) 使 \(p_t=p\) ,指数为 \(a_t\)

    其中 \(\phi(i)=i\times \prod_{j=1}^{k}(1-\frac{1}{p_j})\)

    那么对于 \(m=\prod_{s=1}^{k}p_{s}^{a_s}=p_{1}^{a_1}p_{2}^{a_2}\cdots p_{t}^{a_t+1}\cdots p_{k}^{a_k}\) ,可得到

    \(\phi(m)=m\times \prod_{s=1}^{k}(1-\frac{1}{p_s})=p\times i\times \prod_{s=1}^{k}(1-\frac{1}{p_s})\)

    可以发现右边的求积和 \(\phi(i)\) 形式一模一样,直接带入,就得到 \(\phi(m)=p\times \phi(i)\)

  2. 如果 \(i\ \%\ prime[j]!=0\) ,即 \(i\)\(prime[j]\) 互质,即\(gcd(i,prime[j])= 1\),那么利用性质1、3,得

    \(\phi(m)=\phi(prime[j])\times \phi(i)=(prime[j]-1)\times \phi(i)\)

C++ 代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;
int vis[N];// 记录是否筛掉
int prime[N], cnt; // 记录质因子,cnt是prime数组的个数
int phi[N];// 记录欧拉函数
long long sum[N];// 记录1~i的欧拉函数的和,记得开long long

int main()
{
    int n; cin >> n;
    // 一般的,我们定义1的欧拉函数是1
    phi[1] = 1;
    sum[1] = 1;
    for (int i = 2; i <= n; i++){
        if (!vis[i]) {// 没有遇到过,一定是最小质因数
            prime[cnt++] = i;
            phi[i] = i - 1;// 质数n的欧拉函数是n-1
        }
        for (int j = 0; i * prime[j] <= n; j++){
            int m = i * prime[j];
            vis[m] = 1;// 筛掉合数
            // 以下公式可看证明
            if (i % prime[j] == 0){
                phi[m] = prime[j] * phi[i];
                break;
            }
            else{
                phi[m] = (prime[j] - 1) * phi[i];
            }
        }
        sum[i] = sum[i - 1] + phi[i];
    }
    cout << sum[n] << endl;
}


P2158 [SDOI2008] 仪仗队

SDOI2008 仪仗队 - 洛谷

题目描述

作为体育委员,C 君负责这次运动会仪仗队的训练。仪仗队是由学生组成的 \(N \times N\) 的方阵,为了保证队伍在行进中整齐划一,C 君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。

现在,C 君希望你告诉他队伍整齐时能看到的学生人数。

输入格式

一行,一个正整数 \(N\)

输出格式

输出一行一个数,即 C 君应看到的学生人数。

输入输出样例 #1

输入 #1

4

输出 #1

9

说明/提示

对于 \(100 \%\) 的数据,\(1 \le N \le 40000\)

算法

这里利用了线性筛求欧拉函数的方法。

这道题一看好像是点到点进行模拟,实际上并非这么麻烦。

那么怎么讲这道题与欧拉函数扯上关系呢?关键在于 \(gcd(a,b)=1\)

其中 \(a\) 为水平距离,\(b\) 为竖直距离,我们不难发现,能看见的点 \(a\)\(b\) 一定互质

给定方阵人数 \(n\) ,如果 \(n=1\) ,结果即为0。

这里要查询可看到的人数,实际上就是查询有多少互质的 \(a\)\(b\) ,这里需要对 \(1\sim n\)欧拉函数求和,也就是计算不同的 \(a\) (或 \(b\))有多少 \(b\)(或 \(a\))互质,记作 \(sum[n]\)

观察规律,可以发现结果即为 \(2\times sum[n-1]+1\)

因为两个数互质就必须排除1和自身。对于不同的 \(a\)\(b\) ,计算范围只有 \(2\sim n-1\) 。考虑对称性,共有 \(2\times sum[n-1]\) 个情况。最后考虑对角线上的,结果再加个1,即为最终答案。

C++ 代码

#include <bits/stdc++.h>
using namespace std;
const int N = 4e4 +5;
int vis[N];
int p[N], cnt;
int phi[N];
int sum[N];

void solve(int n)
{
    phi[1] = 1;
    sum[1] = 1;
    for (int i = 2; i <= n; i++){
        if (!vis[i]){
            vis[i] = 1;
            p[cnt++] = i;
            phi[i] = i - 1;
        }
        for (int j = 0; p[j] * i <= n; j++){
            int m = p[j] * i;
            vis[m] = 1;
            if (i % p[j] == 0){
                phi[m] = p[j] * phi[i];
                break;
            }
            else phi[m] = (p[j] - 1) * phi[i];
        }
        sum[i] = sum[i - 1] + phi[i];
    }
    if (n == 1) cout << 0 << endl;
    else cout << 2 * sum[n - 1] + 1 << endl;
}

int main()
{
    int n; cin >> n;
    solve(n);
    return 0;
}
posted @ 2025-02-12 20:22  AKgrid  阅读(13)  评论(0)    收藏  举报