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

欧拉函数

【互质】

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;
}
相关推荐
小林rr9 小时前
深入探索 C++:现代特性、工程实践与性能优化全解
java·c++·性能优化
Xの哲學9 小时前
Linux 软中断深度剖析: 从设计思想到实战调试
linux·网络·算法·架构·边缘计算
暴风游侠9 小时前
如何进行科学的分类
笔记·算法·分类
羊小猪~~9 小时前
【QT】-- QT基础类
开发语言·c++·后端·stm32·单片机·qt
leaves falling10 小时前
冒泡排序(基础版+通用版)
数据结构·算法·排序算法
C雨后彩虹10 小时前
无向图染色
java·数据结构·算法·华为·面试
坚持就完事了10 小时前
扫描线算法
算法
努力写代码的熊大10 小时前
深入探索C++关联容器:Set、Map、Multiset与Multimap的终极指南及底层实现剖析
开发语言·c++
鱼跃鹰飞10 小时前
Leetcode尊享面试100题:252. 会议室
算法·leetcode·面试
程序员-King.10 小时前
二分查找——算法总结与教学指南
数据结构·算法