筛法求欧拉函数,求约数,求约数和

完全平方数的质因子的指数一定为偶数。
别问我为什么写

筛质数:埃氏筛法与线性筛法

线性筛法(又称欧拉筛法)

算法步骤

  1. 从小到大枚举每个数

• 如果当前数没划掉,必定是质数,记录该质数。

• 枚举已记录的质数(如果合数已越界则中断)

  1. 合数处理

• 条件i % p == 0,保证合数只被最小质因子划掉

• 若i是质数,则最多枚举到自身中断

• 若i是合数,则最多枚举到自身的最小质数中断

示例

• n=30

i = 2:p[2];v[4];(2%2)break

i = 3:p[3];v[6,9];(3%3)break

i = 4:v[8];(4%2)break

i = 5:p[5];v[10,15,25];(5%5)break

i = 6:v[12];(6%2)break

i = 7:p[7];v[14,21];(35>30)

i = 8:v[16];(8%2)break

i = 9:v[18,27];(9%3)break

i = 10:v[20];(10%2)break

i = 11:p[11];v[22];(33>30)

i = 12:v[24];(12%2)break

i = 16:(32>30)

代码实现

const int N = 100000010;
int vis[N];  // 划掉合数
int prim[N]; // 记录质数
int cnt;     // 质数个数

void get_prim(int n) { // 线性筛法
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) prim[++cnt] = i;
        for (int j = 1; i * prim[j] <= n; j++) {
            vis[i * prim[j]] = 1;
            if (i % prim[j] == 0) break; // 越界中断
        }
    }
}

代码解释

const int N = 100000010;:定义数组大小。

int vis[N];:用于标记合数。

int prim[N];:用于记录质数。

int cnt;:记录质数的个数。

void get_prim(int n):实现线性筛法的函数。

for (int i = 2; i <= n; i++):从2开始遍历到n。

if (!vis[i]) prim[++cnt] = i;:如果当前数未被标记为合数,则记录为质数。

for (int j = 1; i * prim[j] <= n; j++):遍历所有质数因子。

vis[i * prim[j]] = 1;:标记合数。

if (i % prim[j] == 0) break;:如果当前数能被质数因子整除,则中断循环。
筛法求欧拉函数

欧拉函数的定义

1到n中与n互质的数的个数称为欧拉函数,记为φ(n)。

欧拉函数的性质

  1. 若p是质数,则φ§=p-1

  2. 若p是质数,则φ(pk)=(p-1)p(k-1)

  3. 积性函数:若gcd(m,n)=1,则φ(mn)=φ(m)φ(n)

欧拉函数的计算公式

由唯一分解定理 n=Π(从1到s)p_iα_i=p_1α_1 * p_2^α_2 * … * p_s^α_s,

得φ(n)=n * Π(从1到s)(p_i^(α_i-1) * (p_i-1))

筛法求欧拉函数

若i是质数,phi[i]=i-1。

在线性筛中,每个合数m都是被最小的质因子筛掉的。

设p_j是m的最小质因子,则m通过m=p_j * i筛掉。

  1. 若i能被p_j整除,则i包含了m的所有质因子。
    例,φ(12)=φ(2 * 6)=2 * φ(6)

  2. 若i不能被p_j整除,则i和p_j是互质的。
    例,φ(75)=φ(3 * 25)=(3-1) * φ(25)

代码实现

const int N = 1000010;
int p[N], vis[N], cnt;
int phi[N];

void get_phi(int n) { // 筛法求欧拉函数
    phi[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            p[cnt++] = i;
            phi[i] = i - 1;
        }
        for (int j = 0; i * p[j] <= n; j++) {
            int m = i * p[j];
            vis[m] = 1;
            if (i % p[j] == 0) {
                phi[m] = p[j] * phi[i];
                break;
            } else {
                phi[m] = (p[j] - 1) * phi[i];
            }
        }
    }
}

示例

• n=30

• i=2:p[2],phi[2]=1

• i=3:p[3],phi[3]=2

• i=4:v[6],phi[6]=(2-1) * phi[3]=2

• i=5:p[5],phi[5]=4

• i=6:v[12],phi[12]=2 * phi[6]=4

• i=7:p[7],phi[7]=6

• i=8:v[16],phi[16]=2 * phi[8]=8

• i=9:v[18],phi[18]=(2-1) * phi[9]=6

• i=10:v[20],phi[20]=2 * phi[10]=8

• …

• i=16:(32>30)

筛法求约数和

问题描述

给定一个数 ( n ) (( n \leq 1 \times 10^6 )),输出从 1 到 ( n ) 每个数的约数和。

以16来举例子,其质因子为2指数为4.

所以其约数之和为(20+21+22+23+2^4)=31
这就是约数之和定理。

回顾一下 我们要做的就是将一个数求出他每一个质因子并记录其指数情况。之后将其拿出来先相加再做乘法就ok了

例子

  • ( 12 = 2^2 \times 3^1 )
  • ( f(12) = (1 + 2 + 4) \times (1 + 3) = 7 \times 4 = 28 )

算法步骤

  1. 初始化:
    • g[1] = f[1] = 1
  2. 对于每个 ( i ) 从 2 到 ( n ):
    • 如果 ( i ) 是质数(即 vis[i] 为 0),则:
      • p[++cnt] = i
      • g[i] = f[i] = i + 1
    • 对于每个 ( j ) 从 1 到 ( n ):
      • 计算 ( m = i \times p[j] )
      • 标记 ( m ) 为已访问(vis[m] = 1
      • 如果 ( i ) 能被 ( p[j] ) 整除:
        • ( g[m] = g[i] \times p[j] + 1 )
        • ( f[m] = f[i] / g[i] \times g[m] )
        • 跳出循环
      • 否则:
        • ( g[m] = p[j] + 1 )
        • ( f[m] = f[i] \times g[m] )

代码实现

const int N = 1000010;
int p[N], vis[N], cnt;
int g[N], f[N];

void get_f(int n) {
    g[1] = f[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            p[++cnt] = i;
            g[i] = f[i] = i + 1;
        }
        for (int j = 1; i * p[j] <= n; j++) {
            int m = i * p[j];
            vis[m] = 1;
            if (i % p[j] == 0) {
                g[m] = g[i] * p[j] + 1;
                f[m] = f[i] / g[i] * g[m];
                break;
            } else {
                g[m] = p[j] + 1;
                f[m] = f[i] * g[m];
            }
        }
    }
}

笔记总结

  • 使用筛法求约数和,通过质数分解和乘法原理计算每个数的约数和。
  • 算法复杂度较低,适用于大规模数据。
  • 代码实现中,g[i] 表示 ( i ) 的最小质因子的 1 + ( p^1 ) + … + ( p^k ),f[i] 表示 ( i ) 的约数和。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值