数论学习笔记

快速幂

快速幂可以用 \(O(\log b)\) 的时间复杂度求解 \(a^b \bmod\ p\)

每次检查当前 \(b\) 二进制下的最后一个为是否为 \(1\),为 \(1\) \(ans\) 就乘上当前的 \(a\)。每次需要将 \(a\) 乘上 \(a\),并将 \(b\) 右移一位。

没什么例题可讲,直接放代码。

代码:

#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
int a, b, mod;
int qpow(int a, int b)
{
	int ans = 1;
	while(b)
	{
		if(b & 1) ans = (ans * a) % mod;
		a = (a * a) % mod;
		b >>= 1;
	}
	return ans;
}//快速幂
signed main()
{
	cin >> a >> b >> mod;
	cout << a << '^' << b << " mod " << mod << '=' << qpow(a, b);
	return 0;
}

逆元

求逆元通常有以下 \(3\) 种方法:

  • 费马小定理:若模数 \(p\) 为质数,则 \(x\) 在模 \(p\) 意义下的乘法逆元为 \(x ^ {p - 2} \bmod\ p\)
  • 扩展欧几里得:\(a\) 在模 \(b\) 意义下的乘法逆元相当于的同余方程 \(a x \equiv 1 \pmod {b}\) 的最小正整数解,用扩欧求解。
  • 递推求逆元:\(inv_x\) 表示 \(x\) 在模 \(p\) 意义下的乘法逆元,则 \(inv_x = (p - \frac{p}{x})\times inv_{p \bmod\ x}\bmod\ p\)

例题

P3811 【模板】模意义下的乘法逆元

逆元模板,但是费马小定理和扩展欧几里得求逆元会超时,需要用 \(O(n)\) 的递推求逆元方法。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e6 + 10;
int inv[N];
int n, p;
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> p;
	inv[1] = 1;
	for(int i = 2;i <= n;i ++) inv[i] = (p - p / i) * inv[p % i] % p;//递推式
    for(int i = 1;i <= n;i ++) cout << inv[i] << '\n';
	return 0;
}

Lucas 定理

\(n,k\) 为正整数,\(p\) 为质数:

\[\binom{n}{k} \equiv \binom{n \bmod p}{k \bmod p} \times \binom{\left\lfloor n/p \right\rfloor}{\left\lfloor k/p \right\rfloor} \pmod{p} \]

例题

P3807 【模板】卢卡斯定理/Lucas 定理

Lucas 定理模板,预处理出阶乘和逆元然后直接套模板。

#include <bits/stdc++.h>
#define int long long
#define double long double 
using namespace std;
const int N = 1e5 + 10;
int n, m, p;
int fact[N], inv[N];//fact 数组是阶乘,inv 数组是逆元
int qpow(int a, int b, int mod)
{
	int ans = 1;
	while(b)
	{
		if(b & 1) ans = (ans * a) % mod;
		a = (a * a) % mod;
		b >>= 1;
	}
	return ans;
}//快速幂
int C(int n, int k, int mod)
{
	if(k < 0 || k > n) return 0;
	return fact[n] * inv[k] % mod * inv[n - k] % mod;
}//计算组合数
int Lucas(int n, int k, int mod)
{
	if(k == 0) return 1;
	return C(n % mod, k % mod, mod) * Lucas(n / mod, k / mod, mod) % mod;
}//Lucas 定理模板
signed main()
{
	int T;
	cin >> T;
	while(T --)
	{
		cin >> n >> m >> p;
		fact[0] = 1;
		inv[0] = 1;
		for(int i = 1;i <= N - 10;i ++)
		{
			fact[i] = fact[i - 1] * i % p;
			inv[i] = qpow(fact[i], p - 2, p);
		}
		cout << Lucas(n + m, n, p) << '\n';
	}
	return 0;
} 
posted @ 2025-08-13 09:14  Loyal_Soldier  阅读(12)  评论(0)    收藏  举报