数学部分学习笔记
数学部分学习笔记
数论
唯一分解定理
定理
对于任意正整数
其中
约数个数定理
对于任意正整数
约数和公式
对于任意正整数
裴蜀定理
采用常用的形式描述:
对于方程
扩展欧几里德算法
- 解方程
运用以下代码求出一个特解
void exgcd(int a, int b, int &x, int &y) {
if (b == 0)
return x = 1, y = 0, void();
exgcd(b, a % b, y, x);
y = y - a / b * x;
}
则方程最小正整数解
- 解方程
记
a. 若
b. 若
方程的最小正整数解就是
逆元
求
费马小定理求逆元
当
代码:
int qpow(int x, int y) {
int ans = 1;
while (y) {
if (y & 1)
ans = ans * x % p;
x = x * x % p;
y >>= 1;
}
return ans;
}
扩展欧几里德定理求逆元
当
代码:
void exgcd(int a, int b, int &x, int &y) {
if (b == 0)
return x = 1, y = 0, void();
exgcd(b, a % b, y, x);
y -= a / b * x;
}
线性递推求逆元
当需要线性递推求逆元时,采用以下代码求逆元。
inv[1] = 1;
for (int i = 2; i <= n; ++i) {
inv[i] = (long long)(p - p / i) * inv[p % i] % p;
}
线性同余方程
形如
我们将原方程转化为
将原方程两边同
记
欧拉函数
求单个数欧拉函数的求法:
int solve(int n) {
int ans = n;
for (int i = 2; i * i <= n; i++)
if (n % i == 0) {
ans = ans / i * (i - 1);
while (n % i == 0)
n /= i;
}
if (n > 1)
ans = ans / n * (n - 1);
return ans;
}
筛法求欧拉函数:
vector<int>v;
int phi[N];
bitset<N>pri;
int n;
void get_p() {
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if(!pri[i]) {
v.push_back(i);
phi[i] = i - 1;
}
for(int j = 0; j < (int)v.size() && v[j] * i <= n; j++) {
pri[i * v[j]] = true;
if(i % v[j] == 0) {
phi[i * v[j]] = phi[i] * v[j];
break;
}
phi[i * v[j]] = phi[i] * phi[v[j]];
}
}
}
欧拉定理
欧拉定理
若
扩展欧拉定理
中国剩余定理
中国剩余定理
中国剩余定可求解如下形式的一元线性同余方程组(其中
具体解法:
-
计算所有模数的积
-
对于第
个方程:a. 计算
b.计算
在模 意义下的逆元c.计算
(不要对 取模) -
方程组在模
意义下的唯一解为
实现:
int CRT(int m[], int r[]) {
int M = 1, ans = 0;
for(int i = 1; i <= n; i++)
M *= m[i];
for(int i = 1; i <= n; i++) {
int c = M / m[i], x, y;
exgcd(c, m[i], x, y);
ans = (ans + r[i] * c * x % M + M) % M;
}
return (ans + M) % M;
}
扩展中国剩余定理
当方程组中每一个模数
考虑两个方程的情况。
设两个方程分别是
将它们转化为不定方程:
由 裴蜀定理,当
其他情况下,可以通过 扩展欧几里得算法 解出来一组可行解
则原来的两方程组成的模方程组的解为
然后多个方程时两两合并即可。
代码:
int md[N], rem[N];//存模数和余数
int qmul(int a, int b, int p) {//龟速乘
int ans = 0;
while (b) {
if (b & 1)
ans = (ans + a) % p;
a = (a + a) % p;
b >>= 1;
}
return ans;
}
int exgcd(int a, int b, int &x, int &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
int gcd = exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - a / b * y;
return gcd;
}
signed main() {
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; i++)
cin >> md[i] >> rem[i];
int X = rem[1];//存解
int LCM = md[1];//存前 i - 1 个方程模数的lcm
for(int i = 2; i <= n; i++) {
int p = md[i];
int res = ((rem[i] - X) % p + p) % p;
int x, y, Gcd = exgcd(LCM, p, x, y);
if(res % Gcd) {//判断无解
puts("-1");
return 0;
}
x = qmul(x, res / Gcd, p);
X += x * LCM;
LCM = p / Gcd * LCM;
X = (X % LCM + LCM) % LCM;
}
cout << X << "\n";
return 0;
}
排列组合
排列组合三大方法
插板法
不允许为空的插板
有
公式:
允许为空的插板
有
公式:
插空法
若有
公式:
先计算
捆绑法
若有
公式:
将所有
二项式定理
二项式反演
形式1
记
若已知
若已知
形式2
记
若已知
若已知
或许可以把它理解成一种容斥的变形?
二项式反演的主要用途是在把所求答案是恰好,但至少/不多于这样的答案好求时可以转化答案进行计算。
卢卡斯定理
若
代码:
int C(int n, int m) {
if(m > n)
return 0;
return fac[n] * qpow(fac[m] * fac[n - m] % p, p - 2) % p;
}
int Lucas(int n, int m) {
if(m > n)
return 0;
if(!m)
return 1;
return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}
错排问题
错位排列是没有任何元素出现在其有序位置的排列。即,对于
递推公式:
常用公式
其中
数学方法
高斯消元
一般使用高斯-约旦消元法
// Gauss_Jordan 模板
#include <bits/stdc++.h>
#define N 105
using namespace std;
const double eps = 1e-6;
int n;
double a[N][N];
bool gauss_jordan() {
for (int i = 1; i <= n; i++) {
int r = i;
for (int k = i; k <= n; k++)
if (fabs(a[k][i]) > eps) {
r = k;
break;
}
if (r != i)
swap(a[r], a[i]);
if (fabs(a[i][i]) < eps)
return false;
for (int k = 1; k <= n; k++) {
if (k == i)
continue;
double t = a[k][i] / a[i][i];
for (int j = i; j <= n + 1; j++)
a[k][j] -= t * a[i][j];
}
}
for (int i = 1; i <= n; i++)
a[i][n + 1] /= a[i][i];
return true;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n + 1; j++)
scanf("%lf", &a[i][j]);
if (gauss_jordan())
for (int i = 1; i <= n; i++)
printf("%.2lf\n", a[i][n + 1]);
else
puts("No Solution");
return 0;
}
BSGS
给定一个质数
#include <bits/stdc++.h>
#define int long long
using namespace std;
int p, a, b;
int BSGS() {
unordered_map<int, int>mp;
a %= p;
b %= p;
int s = ceil(sqrt(p));
int t = b;
mp[b] = 0;
for (int i = 1; i <= s; i++) {
t = t * a % p;
mp[t] = i;
}
int x = 1;
for (int i = 1; i <= s; i++)
x = x * a % p;
t = 1;
for (int i = 1; i <= s; i++) {
t = t * x % p;
if (mp.count(t))
return i * s - mp[t];
}
return -1;
}
signed main() {
cin >> p >> a >> b;
int tmp = BSGS();
if(~tmp)
cout << tmp << "\n";
else
puts("no solution");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律