蓝桥杯算法集训 - Week 6:数论基础算法

蓝桥杯算法集训 - Week 6

本系列随笔用于整理AcWing题单——《蓝桥杯集训·每日一题2024》的系列题型及其对应的算法模板。

一、质数(素数)

质数数论复习参考:素数 - OI Wiki

Ⅰ、代码模板

试除法判断质数

static boolean isPrime(int x) {
    if (x < 2)
		return false;
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0)
            return false;
    return true;
}

线性筛法求素数

static int cnt;
static int[] primes = new int[N]; // primes[]存储所有素数
static boolean[] st = new boolean[N]; // st[x]存储x是否被筛掉

static void getPrimes(int n) {
    for (int i = 2; i <= n; i ++ ) {
        if (!st[i])
			primes[cnt++] = i;
        for (int j = 0; primes[j] <= n / i; j ++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0)
				break;
        }
    }
}

Ⅱ、计数质数

204. 计数质数 - 力扣(LeetCode)

计数质数

class Solution {
    public int countPrimes(int n) {
        int ans = 0;
        for (int i = 2; i < n; ++i) {
            ans += isPrime(i) ? 1 : 0;
        }
        return ans;
    }

    public boolean isPrime(int x) {
        for (int i = 2; i * i <= x; ++i) {
            if (x % i == 0) {
                return false;
            }
        }
        return true;
    }
}

二、快速幂

快速幂算法复习参考:快速幂 - OI Wiki

Ⅰ、代码模板

// 快速幂(求a^b mod p)
static int qmi(int a, int b, int p) {
	int res = 1 % p;
	while (b != 0) {
		if ((b & 1) == 1)
			res = res * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return res;
}

Ⅱ、转圈游戏

504. 转圈游戏 - AcWing题库

转圈游戏

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		int k = sc.nextInt();
		int x = sc.nextInt();
		sc.close();
		
		// 编号为x的小朋友每轮走m步走了10^k轮的位置为:(x + m * 10^k) % n
		System.out.println((x + (long) qmi(10, k, n) * m) % n);
	}
	
	// 快速幂(求a^b mod p)
	static int qmi(int a, int b, int p) {
		int res = 1 % p;
		while (b != 0) {
			if ((b & 1) == 1)
				res = res * a % p;
			a = a * a % p;
			b >>= 1;
		}
		return res;
	}
}

三、组合计数

数学原理分析:排列组合 - OI Wiki

Ⅰ、代码模板

递推公式求组合数

// dp[i][j] 表示从 i 个苹果中选 j 个的方案数
for (int i = 0; i < N; i++) {
	for (int j = 0; j <= i; j++) {
		if (j == 0)
			dp[i][j] = 1;
		else
			dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1]) % MOD;
	}
}

通过预处理逆元的方式求组合数

static final int N = 100010, MOD = 1000000007;
static long[] fact = new long[N], infact = new long[N];

// 快速幂模板
static int qmi(int a, int b, int p) {
	long res = 1;
	while (b > 0) {
		if ((b & 1) != 0)
			res = res * a % p;
		a = (int) ((long) a * a % p);
		b >>= 1;
	}
	return (int) res;
}

public static void main(String[] args) {
	// 预处理阶乘的余数和阶乘逆元的余数
	fact[0] = infact[0] = 1;
	for (int i = 1; i < N; i++) {
		fact[i] = fact[i - 1] * i % MOD;
		infact[i] = infact[i - 1] * qmi(i, MOD - 2, MOD) % MOD;
	}
	// 求组合数
	int res = fact[a] * infact[a - b] % MOD * infact[b] % MOD;
}

Ⅱ、组合数问题

523. 组合数问题 - AcWing题库

组合数问题

import java.util.Scanner;

public class Main {
	static final int N = 2010;
	static int t, k;
	static int[][] dp = new int[N][N], sum = new int[N][N];
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		t = sc.nextInt();
		k = sc.nextInt();
		// 递推法预处理组合数数组
		for (int i = 0; i < N; i++) {
			for (int j = 0; j <= i; j++) {
				if (j == 0)
					dp[i][j] = 1 % k;
				else
					dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1]) % k;
				// 若组合数能被k整除则将前缀和初始数组记为1
				if (dp[i][j] == 0)
					sum[i][j] = 1;
			}
		}
		
		// 初始化能被k整除的前缀和数组
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (i != 0)
					sum[i][j] += sum[i - 1][j];
				if (j != 0)
					sum[i][j] += sum[i][j - 1];
				if (i != 0 && j != 0)
					sum[i][j] -= sum[i - 1][j - 1];
			}
		}
		// 输出结果
		while (t-- != 0) {
			int n = sc.nextInt();
			int m = sc.nextInt();
			System.out.println(sum[n][m]);
		}
		sc.close();
	}
}

四、约数与质因数

数论基础算法参考:数论基础 - OI Wiki

Ⅰ、代码模板

欧几里得算法

// 求 a 和 b 的最大公约数
static int gcd(int a, int b) {
	return b == 0 ? a : gcd(b, a % b);
}

试除法求约数

// 求 x 所有约数
static List<Integer> getDivisors(int x) {
	List<Integer> res = new ArrayList<>();
	for (int i = 1; i <= x / i; i++) {
		if (x % i == 0) {
			res.add(i);
			if (i != x / i)
				res.add(x / i);
		}
	}
	Collections.sort(res);
	return res;
}

试除法分解质因数

// 输出 x 的所有质因数
static void divide(int x) {
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0) {
            int s = 0;
            while (x % i == 0) {
				x /= i;
				s ++ ;
			}
			System.out.println(i + " " + s);
        }
    if (x > 1)
		System.out.println(x + " " + 1);
}

Ⅱ、公约数

4199. 公约数 - AcWing题库

公约数

思路:如果是 a 和 b 的公约数,那么一定是 a, b 最大公约数的约数。

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

public class Main {
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		PrintWriter pw = new PrintWriter(System.out);
		String[] split = br.readLine().split(" ");
		int a = Integer.parseInt(split[0]);
		int b = Integer.parseInt(split[1]);
		int q = Integer.parseInt(br.readLine());

		// 预处理获得a, b的最大公约数的所有约数
		List<Integer> divisors = getDivisors(gcd(a, b));

		while (q-- != 0) {
			split = br.readLine().split(" ");
			int L = Integer.parseInt(split[0]), R = Integer.parseInt(split[1]);
			
			// 二分法求解
			int l = 0, r = divisors.size() - 1;
			while (l < r) {
				int mid = l + (r - l + 1) / 2;
				if (divisors.get(mid) <= R)
					l = mid;
				else
					r = mid - 1;
			}
			
			if (divisors.get(r) >= L)
				pw.println(divisors.get(r));
			else
				pw.println(-1);
		}
		pw.flush();
		pw.close();
		br.close();
	}

	// 试除法求所有约数
	static List<Integer> getDivisors(int x) {
		List<Integer> res = new ArrayList<>();
		for (int i = 1; i <= x / i; i++) {
			if (x % i == 0) {
				res.add(i);
				if (i != x / i)
					res.add(x / i);
			}
		}
		Collections.sort(res);
		return res;
	}

	// 求最大公约数
	static int gcd(int a, int b) {
		return b == 0 ? a : gcd(b, a % b);
	}
}
posted @ 2024-04-08 21:56  TfiyuenLau  阅读(20)  评论(0编辑  收藏  举报