蓝桥杯 筛数(欧拉函数+线性筛)

问题描述:

炫炫学了筛法之后,很想用筛法求欧拉函数。他决定求 1 1 1 N N N 的所有数的欧拉函

数值。

输入格式

输入的第一行包含 1 1 1 个整数 n n n

输出格式

输出若干行,每行包含一个整数,第 i i i 行表示 i i i 的欧拉函数值

样例输入

2

样例输出

1
1

数据规模与约定

n ≤ 500000 n \le 500000 n500000

前置知识

欧拉函数的定义

1 ∼ N 1∼N 1N 中与 N N N 互质的数的个数被称为欧拉函数,记为 ϕ ( N ) ϕ(N) ϕ(N)

若在算数基本定理中, N = p 1 a 1 p 2 a 2 … p m a m N=p_1^{a_1}p^{a_2}_2…p^{a_m}_m N=p1a1p2a2pmam

则: ϕ ( N ) = N × p 1 − 1 p 1 × p 2 − 1 p 2 × . . . × p m − 1 p 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×p1p11×p2p21×...×pmpm1

欧拉函数的证明

解题思路

利用线性筛法求出每个数的欧拉函数,质数的欧拉函数值就是 P − 1 P - 1 P1

对于一个数 i i i ,和一个质数 p p p ,有以下两种情况:

情况1:

i   m o d   p = = 0 i \bmod p == 0 imodp==0 ,此时 φ ( i ∗ p ) = φ ( i ) × p \varphi(i*p) = \varphi(i) \times p φ(ip)=φ(i)×p证明如下:

如果 p p p i i i 的一个因子, N N N 的某一项可以变成 p k a k + 1 p_k^{a_k+1} pkak+1,即

φ ( p k a k + 1 ) = p k a k + 1 − p k a k = p k a k + 1 × ( 1 − 1 p k ) = p k × p k a k × ( 1 − 1 p k ) \varphi(p_k^{a_k+1}) = p_k^{a_k+1} - p_k^{a_k} = p_k^{a_k+1} \times (1 - \frac{1}{p_k}) = p_k \times p_k^{a_k} \times (1-\frac{1}{p_k}) φ(pkak+1)=pkak+1pkak=pkak+1×(1pk1)=pk×pkak×(1pk1)

那么:

φ ( i ∗ p ) = p k × N × ( 1 − 1 p 1 ) × . . . × ( 1 − 1 p m ) = p × φ ( i ) \varphi(i*p) = p_k \times N \times (1-\frac{1}{p_1}) \times ... \times (1-\frac{1}{p_m}) = p \times \varphi(i) φ(ip)=pk×N×(1p11)×...×(1pm1)=p×φ(i)

得证。

情况二:

i   m o d   p ≠ 0 i \bmod p \ne 0 imodp=0,此时 φ ( i ∗ p ) = φ ( i ) × ( p − 1 ) \varphi(i*p) = \varphi(i) \times (p-1) φ(ip)=φ(i)×(p1)证明如下:

其实就是 N N N 多了一个质因数 N = p 1 a 1 × . . . × p k a k × p k + 1 N = p_1^{a_1} \times ... \times p_k^{a_k} \times p_{k+1} N=p1a1×...×pkak×pk+1

所以 φ ( i ∗ p ) = φ ( i ) ∗ φ ( p ) \varphi(i*p) = \varphi(i) * \varphi(p) φ(ip)=φ(i)φ(p) ,而 φ ( p ) = p − 1 \varphi(p) = p-1 φ(p)=p1

故: φ ( i ∗ p ) = φ ( i ) × ( p − 1 ) \varphi(i*p) = \varphi(i) \times (p-1) φ(ip)=φ(i)×(p1),结论得证

代码展示

import java.io.*;
import java.util.*;

public class Main {
	static final int N = 500010;
	static int[] primes = new int[N];
	static int[] phi = new int[N];
	static boolean[] st = new boolean[N];
	static int cnt;
	
	static void get_eulers(int n) {
	    for (int i = 2; i <= n; i ++ ) {
	        if (!st[i]) {
	            primes[cnt++] = i;
	            phi[i] = i-1;
	        }
	        for (int j = 0; primes[j] <= n/i; j ++ ) {
	            st[i*primes[j]] = true;
	            if (i % primes[j] == 0) {
	                phi[i*primes[j]] = primes[j] * phi[i];
	                break;
	            }
	            phi[i*primes[j]] = phi[i] * (primes[j]-1);
	        }
	    }
	}

	public static void main(String[] args) throws Exception {
		BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
		PrintWriter pw = new PrintWriter(System.out);
		int n = Integer.parseInt(bf.readLine().split(" ")[0]);
		get_eulers(n);
		phi[1] = 1;
		for (int i = 1; i <= n; i ++ ) pw.println(phi[i]);
		pw.close();
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

取名真难www

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值