蓝桥杯算法集训 - 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;
}
}
}
Ⅱ、计数质数
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;
}
Ⅱ、转圈游戏
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;
}
Ⅱ、组合数问题
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);
}
Ⅱ、公约数
思路:如果是 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);
}
}
本文来自博客园,作者:TfiyuenLau,转载请注明原文链接:https://www.cnblogs.com/tfiyuenlau/p/18122745