算法基础(数论)—欧拉函数

欧拉函数

【互质】

ab 互质:说明 ab 的最⼤公约数是 1 。
【欧拉函数】
• 对于⼀个数 n ,在 1 ∼ n 中,与 n 互质的数的个数,就是欧拉函数,⽤ φ ( n ) 表⽰。
⽐如: φ (1) = 1 , φ (2) = 1 , φ (3) = 2 , φ (4) = 2 , φ (5) = 4 ......

【积性函数】
• 若函数f(n) 满⾜:f(1)=1 ,且f(xy)=f(x)f(y) x,y都属于N* 都成⽴。则 f(n)为积性
函数。
【欧拉函数的性质】

  1. p 为质数,则 φ ( p ) = p − 1 ;
  2. p 为质数,则 φ ( p k ) = ( p − 1) p k −1 ;
  3. 欧拉函数为积性函数: ,其中 。(感兴趣的同学研究⼀下证
    明,因为⽐较繁琐,这⾥不做证明。)
cpp 复制代码
块int phi(int n)
{
int ret = n;
for(int i = 2; i <= n / i; i++)
{
if(n % i == 0)
{
ret = ret / i * (i - 1); // 先除后乘,保证不会溢出
while(n % i == 0) n /= i;
}
}
// 别忘记判断最后⼀个数
if(n > 1) ret = ret / n * (n - 1);
return ret;
}
cpp 复制代码
#include <iostream>
using namespace std;

const int N = 100010;  // 根据需求定义数组大小(需大于输入的n)
int n;                // 目标范围上限
bool st[N];           // 标记是否为合数(true=合数,false=质数)
int p[N], cnt;        // p[]存储质数,cnt是质数计数
int phi[N];           // phi[x]存储x的欧拉函数值

// 线性筛求1~n的欧拉函数
void get_phi() {
    phi[1] = 1;  // 欧拉函数定义:φ(1)=1(1与自身互质)
    // 枚举2~n的所有数
    for (int i = 2; i <= n; i++) {
        // 若i是质数(未被标记)
        if (!st[i]) {
            p[++cnt] = i;       // 加入质数数组
            phi[i] = i - 1;     // 质数的欧拉函数:φ(p)=p-1(1~p-1都与p互质)
        }
        // 用当前质数p[j]筛去i*p[j]
        for (int j = 1; 1LL * i * p[j] <= n; j++) {
            int x = i * p[j];   // 要筛的合数x = i * p[j]
            st[x] = true;       // 标记x为合数
            // 情况1:p[j]是i的最小质因数(i能被p[j]整除)
            if (i % p[j] == 0) {
                // 欧拉函数性质:若p是x的质因数且x = p^k * m(m与p互质),则φ(x)=p*φ(m)
                phi[x] = p[j] * phi[i];
                break;          // 线性筛核心:保证每个数仅被最小质因数筛去,直接break
            }
            // 情况2:p[j]不是i的质因数(i与p[j]互质)
            else {
                // 欧拉函数积性:若a与b互质,则φ(a*b)=φ(a)*φ(b)
                phi[x] = (p[j] - 1) * phi[i];
            }
        }
    }
}

// 测试示例
int main() {
    cin >> n;
    get_phi();
    // 输出1~n的欧拉函数值(可选)
    for (int i = 1; i <= n; i++) {
        cout << "φ(" << i << ") = " << phi[i] << endl;
    }
    return 0;
}

时间复杂度:
与线性筛时间复杂度⼀致,为 O ( n ) 。


1 仪仗队

题⽬来源: 洛⾕
题⽬链接: P2158 [SDOI2008] 仪仗队
难度系数: ★★★★

题目描述

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

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

输入格式

一行,一个正整数 N。

输出格式

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

输入输出样例

输入 #1复制

复制代码
4

输出 #1复制

复制代码
9

说明/提示

对于 100% 的数据,1≤N≤40000。


【解法】

对于坐标 ( i , j ) ,当且仅当 ij 互质的时,才能被看到。
因此,统计出 [1, n − 1] 的欧拉函数之和,乘 2 再加 1 就是结果。


【参考代码】

cpp 复制代码
#include <iostream>
using namespace std;
typedef long long LL;  // 防止数值溢出
const int N = 40010;   // 适配题目中N的范围(通常N≤40000)

int n;                // 输入的方阵边长
bool st[N];           // 标记是否为合数
int p[N], cnt;        // p[]存储质数,cnt是质数计数
int phi[N];           // phi[x]存储x的欧拉函数值

// 线性筛(欧拉筛)预处理1~n的欧拉函数
void get_phi() {
    phi[1] = 1;       // 欧拉函数定义:φ(1)=1
    for (int i = 2; i <= n; i++) {
        // 若i是质数
        if (!st[i]) {
            phi[i] = i - 1;  // 质数的欧拉函数:φ(p)=p-1
            p[++cnt] = i;    // 加入质数数组
        }
        // 用当前质数筛去合数,同时计算欧拉函数
        for (int j = 1; 1LL * i * p[j] <= n; j++) {
            int x = i * p[j];
            st[x] = true;    // 标记为合数
            // 情况1:p[j]是i的最小质因数
            if (i % p[j] == 0) {
                phi[x] = phi[i] * p[j];
                break;       // 线性筛核心:保证每个数仅被最小质因数筛去
            }
            // 情况2:p[j]与i互质(积性性质)
            else {
                phi[x] = phi[i] * (p[j] - 1);
            }
        }
    }
}

int main() {
    cin >> n;
    // 特殊情况:N=1时无学生
    if (n == 1) {
        cout << 0 << endl;
        return 0;
    }
    // 预处理欧拉函数
    get_phi();
    // 统计1~n-1的欧拉函数之和
    LL sum = 0;
    for (int i = 1; i < n; i++) {
        sum += phi[i];
    }
    // 对称乘2 + 正前方1个
    cout << sum * 2 + 1 << endl;
    return 0;
}

2 GCD

题⽬来源: 洛⾕
题⽬链接: P2568 GCD
难度系数: ★★★★

题目描述

给定正整数 n,求 1≤x,y≤n 且 gcd(x,y) 为素数的数对 (x,y) 有多少对。

输入格式

只有一行一个整数,代表 n。

输出格式

一行一个整数表示答案。

输入输出样例

输入 #1复制

复制代码
4

输出 #1复制

复制代码
4

说明/提示

样例输入输出 1 解释

对于样例,满足条件的 (x,y) 为 (2,2),(2,4),(3,3),(4,2)。


数据规模与约定

  • 对于 100% 的数据,保证 1≤n≤107。

来源:bzoj2818。

本题数据为洛谷自造数据,使用 CYaRon 耗时 5 分钟完成数据制作。


【解法】

构造 x , y 数对。
设: gcd( x , y ) = d ,其中 x = ad , y = bd ,那么 gcd( x , y ) = gcd( ad , bd ) = d × gcd( a , b ) 。
可得: gcd( a , b ) = 1 ,也就是 a , b 互质。
其中,x,y 的最⼤公约数为d ,且 d为质数,那么 x,y应该在d , 2 d , 3 d , 4 d ... × 中挑选,且
系数互质。
问题就变成求每⼀个数的欧拉函数。


【参考代码】

cpp 复制代码
#include <iostream>
using namespace std;
typedef long long LL;  // 防止数值溢出
const int N = 1e7 + 10;  // 适配n的最大值(题目中n≤1e7)

int n;                // 输入的正整数n
bool st[N];           // 标记是否为合数
int p[N], cnt;        // p[]存储质数,cnt是质数计数
int phi[N];           // phi[x]存储x的欧拉函数值
LL f[N];              // f[k] = φ(1)+φ(2)+...+φ(k)(前缀和)

// 线性筛(欧拉筛)预处理1~n的欧拉函数+质数数组
void get_phi() {
    phi[1] = 1;       // 欧拉函数定义:φ(1)=1
    for (int i = 2; i <= n; i++) {
        // 若i是质数
        if (!st[i]) {
            phi[i] = i - 1;  // 质数的欧拉函数:φ(p)=p-1
            p[++cnt] = i;    // 加入质数数组
        }
        // 筛合数并计算欧拉函数
        for (int j = 1; 1LL * i * p[j] <= n; j++) {
            int x = i * p[j];
            st[x] = true;
            // 情况1:p[j]是i的最小质因数
            if (i % p[j] == 0) {
                phi[x] = phi[i] * p[j];
                break;  // 线性筛核心:保证每个数仅被最小质因数筛去
            }
            // 情况2:p[j]与i互质(积性性质)
            else {
                phi[x] = phi[i] * (p[j] - 1);
            }
        }
    }
}

int main() {
    cin >> n;
    // 预处理欧拉函数和质数数组
    get_phi();
    
    // 预处理欧拉函数前缀和f[k] = φ(1)+φ(2)+...+φ(k)
    for (int i = 1; i <= n; i++) {
        f[i] = f[i - 1] + phi[i];
    }
    
    LL sum = 0;  // 最终答案
    // 遍历所有质数d,计算每个d对应的数对数量
    for (int i = 1; i <= cnt; i++) {
        int d = p[i];       // 当前质数d
        int k = n / d;      // a,b的上限k = n/d
        sum += f[k] * 2 - 1;// 累加当前d对应的数对数量
    }
    
    cout << sum << endl;
    return 0;
}
相关推荐
Brduino脑机接口技术答疑4 小时前
TDCA 算法在 SSVEP 场景中的训练必要性
人工智能·算法·机器学习·脑机接口
xu_yule4 小时前
算法基础(数学)—数论
c++·算法·数论·最大公约数和最小公倍数·质数的判定·筛质数
Sheep Shaun4 小时前
二叉搜索树(下篇):删除、优化与应用
数据结构·c++·b树·算法
极简车辆控制4 小时前
基于LQR全主动七自由度全车悬架车身姿态控制
算法
superman超哥4 小时前
仓颉借用检查器工作原理深度解析
c语言·开发语言·c++·python·仓颉
s09071364 小时前
常用FPGA实现的图像处理算法
图像处理·算法·fpga开发
core5124 小时前
SVM (支持向量机):寻找最完美的“分界线”
算法·机器学习·支持向量机·svm
TG:@yunlaoda360 云老大4 小时前
华为云国际站代理商的DDM支持哪些拆分算法?
数据库·算法·华为云
qq_430855885 小时前
线代第二章矩阵第五、六、七节矩阵的转置、方阵的行列式、方阵的伴随矩阵
线性代数·算法·矩阵