OI数论基础(未完工)
OI数论基础
——由\(\bf\color{red}{5}\tt\color{blue}{1}\bf\color{green}{7}\)(就是老师自己的OJ)提供思路
1 素数筛法
1.1 朴素板筛法
将每一个数的所有的倍数都筛去,由于素数只能被1和它本身整除,因此素数不会被筛到,计算量为\(n/2+n/3+n/4+...\)这个调和级数,复杂度\(O(nlogn)\)
const int N = 100010;
bool flag[N];
int p[N],tot;
void init(int n) {
for(int i=2;i<=b;++i) {
for(int j=i+i;j<=n;j+=i) {
flag[j] = true;
}
}
for(int i=2;i<=n;++i) {
if(!flag[i]) p[++tot] = i;
}
}
1.2 优化板筛法
我们注意到朴素版筛法里面 \(36\) 会被 \(2,3,4,6,9,12,18\) 这 \(6\) 个数筛到,相 当于被 \(2*18,3*12,4*9,6*6,9*4,12*3,18*2\) 筛到,实际上 \(a∗b\) 跟 \(b∗a\) 是等价的,于是,我们可以通过枚举小的那个因子,来优化一半的计算量, 相当于 \(i∗(i + 1),i∗(i + 2)\) 这样子去筛, 复杂度不变,常数上优化了
void init(int n) {
int up = (int)sqrt(1.0*n) + 1;
for (int i = 2; i <= up; i++) {
for (int j = i * i; j <= n; j += i) {
flag[j] = true;
}
}
for (int i = 2; i <= n; i++) {
if (!flag[i]) {
p[tot++] = i;
}
}
}
1.3 线性筛法
那么存在 \(O(n)\) 的筛法,使得每个数只被筛到一次么? 答案是肯定的。 下面这个代码短小精悍,可以做到每一个数只被其最小的素因子筛选到
void init(int n) {
for (int i = 2; i <= n; i++) {
if (!flag[i]) {
p[tot++] = i;
}
for (int j = 0; j < tot; j++) {
if (i * p[j] > n) {
break;
}
flag[i * p[j]] = true; //p[j]是i * p[j]的最小素因子
if (i % p[j] == 0) { // 再往后p[j]就不是i * p[j]的最小素因子了
break;
}
}
}
}
2 欧几里得
2.1 辗转相减
利用辗转相减法求最大公约数, 即 \(gcd(a,b)\)。假设 \(a > b\), 则 \(gcd(a,b) = gcd(a−b,b)\), 不断的利用大的数减去小的数,就能得到最大公约数。
证明:
\(a = k1 ∗g\)
\(b = k2 ∗g\)
假设 $g $为最大公约数, 那么 \(gcd(k1,k2) = 1\), 设 \(a > b\), 令 \(a = a−b\),
\(a = (k1 −k2)∗g\)
\(b = k2 ∗g\)
我们发现 \(a,b\) 的变化为两个互质的系数在不断相减(始终还是互质), \(g\) 始终是它们的最大公约数,所以当有一个数变成 \(0\) 的时候,另一个数就是 最大公约数
while (a != b) {
if(a > b) {
a -= b;
} else {
b -= a;
}
}
2.2 辗转相除
我们发现当 \(a\) 一直大于 \(b\) 的时候,就会一直减去 \(b\),其实就是变成 \(a\%b\), 于是我们可以利用辗转相除来优化,
while (a && b) {
if (a > b) {
a = a % b;
} else {
b = b % a;
}
}//循环结束后 max(a,b) 就是答案
int gcd(int a, int b) {return !b ? a : gcd(b, a % b);}
3 拓展欧几里得
扩展欧几里得的典型应用是解决形如 \(a∗x + b∗y = c\) 的二元一次方程 的解的存在性问题以及求出特解和通解。
3.1 解的存在性问题
其实我们通过辗转相减就可以观察出来,\(a\),\(b\) 辗转相减的时候相当于 \(a∗x+b∗y\) 中的 \(x\),\(y\) 在不断变化的过程, 比如 \(a−b\),\(a−2∗b\),\(3∗b−a\),我们 通过前面知道这个过程一定能得到最大公约数, 所以 \(a∗x+b∗y = gcd(a,b)\) 一定有解,那么是否 \(gcd(a,b)\neq c\) 的时候就无解呢?首先我们知道 \(c\) 是 \(gcd(a,b)\) 的倍数才可能有解,因为左边一定含有因子 \(gcd(a,b)\),左边等于右 边,那么右边也一定含有 \(gcd(a,b)\),因此我们可以通过两边同时除以最大公 约数得到一个新的式子
\(k1 ∗x + k2 ∗y = c/g. gcd(k1,k2) = 1\)
由辗转相减可得:
\(k1∗x+k2∗y = 1\) 一定有解,所以
\(k1∗x+k2∗y = c/g\) 一定有解,
所以当 \(c\) 是 \(gcd(a,b)\) 的倍数的时候,方程一定有解
3.2 特解与通解
所以我们只需要求解形如
$a∗x + b∗y = gcd(a,b) $
然后将其扩大\(c/gcd(a,b)\)倍就可以解出原方程的解了
观察到这个式子的本质其实就是\(a\)和\(b\)辗转相减的过程,假设已经求出了
\((a-b) * x_1+ b * y = gcd(a,b)\)
的解\(x_1,y_1\),那么变换一下得到\(a* x_1 +b * (y_1-x_1) = gcd(a,b)\),我们可以得出
\(x = x_1, y = y_1 - x_1\)
得到了原方程的解,所以我们可以将原问题变成一个规模更小的子问题,然
后根据子问题的答案推出原问题的答案,考虑到这个辗转相减可以用辗转
相除来替代,我们可以将原问题变成求解
\(b ∗ x_1 + a\%b ∗ y_1 = gcd(a, b)\)
由
\(a\%b = a - a/b ∗ b\)
代入得
\(b ∗ x_1 + (a - a/b ∗ b) ∗ y_1 = gcd(a, b)\)
等价于
\(a ∗ y_1 + b ∗ (x_1 - a/b ∗ y_1) = gcd(a, b)\)
得到
\(x = y_1, y = x_1 - a/b ∗ y_1\)
因此我们只需一直缩小问题规模,直到变成 \(gcd(a, b) ∗ x + 0 ∗ y = gcd(a, b)\),
然后得到一组特解 \((1, 0)\),再将这组特解反推回去,得到一开始的方程的一
组特解。这个过程可以用一个递归函数来实现。
int extgcd(int a, int b, int &x, int &y) {
int d = a;
if(b != 0) {
d = extgcd(b, a % b, x, y);
x -= (a / b) * y;
std::swap(x, y);
} else {
x = 1; y = 0;
}
return d;
}
函数执行完毕后代码里面的 \((x, y)\) 就是 \(a ∗ x + b ∗ y = gcd(a, b)\) 的一组
特解, 通解的变化规律就是 \(x\) 和 \(y\) 的值往相反的方向变化,比如 \(x\) 变大一
些,\(y\) 变小一些,使得答案不变,设这个变化的最小单位值为 \(d1\), \(d2\)
\(a ∗ (x + d_1) + b ∗ (y - d_2) = gcd(a, b)\)
\(a ∗ d_1 = b ∗ d_2\)
两边同除 \(gcd(a, b)\) 令 \(k_1 = a/gcd(a, b)\), \(k_2 = b/gcd(a, b)\)
\(k_1 ∗ d_1 = k_2 ∗ d_2\)
这个时候 \(k_1\), \(k_2\) 互质,显然 \(d_1 = k_2\), \(d_2 = k_1\),可得通解为
\((x + k ∗ d_1, y - k ∗ d_2)\)
即
\((x + k ∗ b/gcd(a, b), y - k ∗ a/gcd(a, b))\)
3.3 关于解的绝对值大小
我们通过递归函数的实现可以观察到,解的绝对值是跟 a, b 的绝对值
同一个级别的
4 排列组合
4.1 排列的定义
从\(n\)个不同元素中,任取\(m\)(\(m\leq n\),\(m\)与\(n\)均为自然数, 下同)个元素按照一定的顺序排成一列,叫做从\(n\)个不同元素中取出\(m\)个元素的一个排列
从\(n\)个不同元素中取出\(m\)(\(m\leq n\))个元素的所有排列的个数,叫做从\(n\)个不同元素中取\(m\)个元素的排列数,用符号\(A_n^m\) 表示。
计算公式
\(A_n^m= \frac{n!}{(n−m)!}\)
4.2 组合的定义
\(n\)个元素中选 \(m\) ( \(m\leq n\) ) 个出来,不排序,不在乎顺序就是 \(C^m_n\) 。
也可表示成 \({n \choose m}\) 的形式。
计算公式
\({C^n_m=\frac{n!}{m!\times (m-n)!}}\)
4.3 基础公式
排列组合的关系为
\(C^m_n×A^m_m=C^m_n×m!=A^n_m\)
对称恒等式
提取/吸收恒等式
加法/归纳恒等式
相伴恒等式
上指标反转
4.4 二项式定理
4.5二项式反演
常用表达
4.5.1恰好和至多的转换
设 \(f_i\) 表示恰好的方案数, \(g_i\) 表示至多的方案数,则有
根据二项式反演得
4.5.2恰好和至少的转换
设 \(f_i\) 表示恰好的方案数, \(g_i\) 表示至少的方案数,则有
根据二项式反演得