【蓝桥杯】2023年第十四届省赛真题-互质的个数-Java-详细解

写在前面

关于互质的定义,可以看我的这篇帖子: 互质
关于欧拉公式,可以看这篇(还没更新,可以看代码中的注释)
关于快速幂算法, 可以看我的这篇(还没更新,可以看代码中的注释)
关于求模运算,可以看我的这篇: 模运算

算法思路

根据欧拉函数,我们可以知道,要求就是 E u l e r ( a b ) Euler(a^{b}) Euler(ab)
然后如果直接求,我们一定会因为因为 a b a^{b} ab 数字太大求欧拉的复杂度太高,因此可以通过欧拉函数的性质( [ E u l e r ( a b ) = E u l e r ( a ) ∗ a b − 1 ] [Euler(a^{b})=Euler(a) * a^{b-1}] [Euler(ab)=Euler(a)ab1]),我们只需要得到底数 a a a的互质个数,再结合快速幂算法简化 a^{b-1}运算, 就可以得到 E u l e r ( a b ) Euler(a^{b}) Euler(ab)

代码如下

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Scanner;

public class main {

	/**
	 * 给定 a, b,求 1 ≤ x < a^b 中有多少个 x 与 a^b 互质。由于答案可能很大, 你只需要输出答案对 998244353 取模的结果。
	 * 
	 * 输入格式: 输入一行包含两个整数分别表示 a, b,用一个空格分隔。
	 * 
	 * 输出格式: 输出一行包含一个整数表示答案。 样例输入 2 5 样例输出 16
	 */

	private static long mod = 998244353L;
	private static long a, b;
	
	// 以下是输入输出流代码, 在某些情况下可以提高通过率,在这个题会导致无法全对, 原因不明
	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static StreamTokenizer st = new StreamTokenizer(br);
	static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
	static long nextLong() throws Exception {
		st.nextToken();
		return (long) st.nval;
	}

	// 主函数
	public static void main(String[] args) throws Exception {
		Scanner sc = new Scanner(System.in);
		a = sc.nextLong();
		b = sc.nextLong();
		long res = Euler(a);
		res = res%mod * Quick_pow(a, b - 1)% mod;
		System.out.print(res);

	}

	// 欧拉函数计算公式
	// 欧拉函数定义式: 对于 n = p1^k1 * p2^k2 * .. * pm^km (p1, p2, p3.. pm 为 n 的质因数)
	// Euler(n) = n * (1-1/p1) * (1-1/p2) * .. * (1-1/pm)

	// 欧拉函数性质: 若 n mod x = 0, 即 n 能被 x 整除, 且 n/x mod x =0, 即 n/x 能够被x 整除, 则 Euler(n)
	// = Euler(n/x) * x
	// 因此 对于 Euler(a^b) = Euler(a^(b-1)) * a = Euler(a^(b-2)) * a^2 = .. = Euler(a)
	// * a^(b-1)

	// 以下函数正是计算: Euler(a)
	private static long Euler(long n) {
		long res = n;
		for (long i = 2; i * i <= n; ++i) {
			if (n % i == 0) {
				res = res / i * (i - 1); // 尽量先除, 再乘, 避免越域风险
				while (n % i == 0) {
					n = n / i;
				}
			}
		}
		if (n > 1) {
			res = res / n * (n - 1);
		}
		return res;
	}

	// 快速求幂算法
	// 如果指数是偶数: 那么指数除以2, 底×底
	// 如果指数是奇数: 那么提取出指数, 再将剩下的指数除以2
	// 以上循环, 直到 剩下的指数 为0
	// 结果: 多个以奇数为指的数相乘
	// 例如: 10^100 = 100^50 = 10000^25 = 10000 * 100000000^12, 循环次数从100降低成了12

	// 特殊性: 这里题目是要求互质的个数与998244353L求模:
	// 根据欧拉函数的公式: Euler(n) = n*(1-1/p1)*(1-1/p2)...*(1-1/pn) (pi为0的质因数)
	// 因此题目的结果表达式: r = Euler(n) % (998244353L) = [Euler(a) * a^(b-1)] % (998244353L)
	// 根据 模的 模乘法分配律: (a*b)%n = [(a%n)*(b%n)]%n = (a%n*b%n)%n
	// r = [Euler(a) % (998244353L) * a^(b-1) % (998244353L)] % (998244353L)
	// 以下函数正是计算: a^(b-1) % (998244353L)

	// 算法思路: 使用快速求幂算法降低循环运行的次数
	// 假设 a^b = a1*a2*a3... an (ai 指数均为1, 且 ai 一定为 a 的 指数倍, 因为 ai 为缩小幂的时候提取出来的)
	// 那么 a^b % (998244353L) = [a1 % (998244353L) * a2 % (998244353L).. * an %
	// (998244353L)] % (998244353L)
	// 由于每个ai 都是 a 的指数倍, 因此不妨设: ai = a^m
	// 再次根据 模的乘法分配律: ai % (998244353L)= a^m % (998244353L) = [a % (998244353L)..*a %
	// (998244353L)]% (998244353L)
	// 即每次提取出底的时候都对其取模, 这样做可以保证不溢出域

	private static long Quick_pow(long a, long b) {
		// a: 底数 b: 指数
		long res = 1; // 结果
		while (b > 0) {
			// 如果指数是偶数
			if (b % 2 == 0) {
				b = b / 2;
				a = a % mod * a % mod;
			} else {
				// 如果 指数为奇数, 提取当前的a
				b = b - 1;
				res = res % mod * a % mod;
			}

		}
		return res % mod;
	}

}

在这里插入图片描述

  • 33
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值