【瞎口胡】基础数学 1 快速幂 整除 最大公约数
写在前面
真的没啥可看的,就当我是给一万年后我校学 OI 的小朋友们写的(如果那时候还有的话)。
普通选手还是别看了,浪费时间,而且体现了我的菜。
符号说明
以下符号都是文中不会解释的。
\(\sum\):求和符号,\(\sum _{i=1}^{n} d_i = d_1 + d_2 + ... + d_n\),\(\sum_{d|n} d=\) \(n\) 的正约数和。
\(\prod\):连乘积符号,\(\prod_{i=1}^{n} i = 1 \times 2 \times ... \times n\)。
\(\text{mod}\):模。\(x \mod y\) 即为 \(x\) 除以 \(y\) 的余数,\(2 \mod 3 = 2,8 \mod 4 = 0,5 \mod 3 = 2\)。
\(\min\{\},\max\{\}\):最小值,最大值。这都不会还来看这篇文章?
快速幂
快速幂利用了整数乘法的结合律。\(a^{b+c} = a^b \times a^c\),利用这个思想把 \(a^c \mod p\) 在 \(O(\log c)\) 的时间内求出。
具体的思想是把 \(c\) 二进制拆分成 \(2^{r_1}+2^{r_2}+2^{r_3}+...2^{r_n}\),其中 \(r_i\) 两两不同。再计算所有 \(a^{2^{r_1}}\) 的乘积,就得到了 \(a^c\)。
模板题 Luogu P1226 Code
# include <bits/stdc++.h>
# define int long long
const int N=100010,INF=0x3f3f3f3f;
int b,p,k;
inline int read(void){
int res,f=1;
char c;
while((c=getchar())<'0'||c>'9')
if(c=='-')f=-1;
res=c-48;
while((c=getchar())>='0'&&c<='9')
res=res*10+c-48;
return res*f;
}
inline int qpow(int d,int p){ d^p mod k
int ans=1;
while(p){
if(p&1){ // 找出 p 中二进制下为 1 的位
ans=ans*d%k;
}
p>>=1,d=d*d%k;
}
return ans%k; // 大多时候可以不用取模 只有 k = 1 的时候需要这么做
}
# undef int
int main(void){
# define int long long
b=read(),p=read(),k=read();
printf("%lld^%lld mod %lld=%lld",b,p,k,qpow(b,p));
return 0;
}
整除
基本知识
对于非负整数 \(a\) 和正整数 \(b\),如果 \(\frac ab\) 是整数(即: \(a\) 除以 \(b\) 的余数是 \(0\)),那么我们称 \(b\) 整除 \(a\),记作 \(b \mid a\);称 \(b\) 是 \(a\) 的约数(或称因数,因子),\(a\) 是 \(b\) 的倍数。例如:\(2 \mid 114514\),\(2\) 是 \(114514\) 的约数,\(114514\) 是 \(2\) 的倍数。
如果 \(b\) 不整除 \(a\),则记作 \(b \nmid a\)。例如 \(114514 \nmid 1919810\)。
\(a\) 除以 \(b\) 的余数记作 \(a \mod b\)。例如 \(9 \mod 7=2,13 \mod 4 =1\)。
-
求出正整数 \(n\) 的所有约数
容易发现 \(n\) 的约数成对出现,若 \(i|n\),则 \(\frac ni|n\)。
考虑枚举 \([1,\sqrt n]\) 范围内的每一个正整数,逐个判断是否是 \(n\) 的约数。
时间复杂度 \(O(\sqrt n)\)。
std::vector <int> A; for(int i=1;i*i<=n;++i){ if(i%n==0){ A.push_back(i); if(i*i!=n){ // 对于 n 是完全平方数的情况 如果不加以判断会被记录两次 A.push_back(n/i); } } }
-
求出 \([1,n]\) 所有正整数的所有约数
可以直接将上面的东西跑 \(n\) 遍,总复杂度 \(O(n\sqrt n)\)。
复杂度不够优秀。考虑枚举 \(1\) 到 \(n\) 的所有正整数 \(x\),将 \(x,2x,···\) 的约数集合中添加一个元素 \(x\)。
这样总复杂度降到了 \(O(n+\frac n2+\frac n3 ···· +1)=O(n \ln n)\)。
std::vector <int> A[MAXN]; for(int i=1;i<=n;++i){ for(rr int j=1;i*j<=n;++j){ A[i*j].push_back(i); } }
如果一个正整数只有 \(1\) 和它本身两个因子,那么我们称它为素数(或质数)。\(1\) 不是质数,\(2\) 是最小的质数。如果一个正整数的因子个数大于 \(2\),则我们称它为合数。\(1\) 也不是合数。最小的合数是 \(4\)。
算术基本定理
当 \(n\) 是正整数且 \(>1\),\(p_i\) 均为不同的素数,\(c_i >0\) 时,有唯一的 \(p\) 和 \(c\) 使得
其中 \(c_i\) 被称为 \(n\) 中素因子 \(p_i\) 的次数(指数),\(p_i\) 被称为 \(n\) 的素因子(质因子)。
证明:如果有两个分解式 \(n=\prod_{i=1}^{m} p_i^{c_i}\) 和 \(n=\prod_{i=1}^{m} {p'}_i^{{c'}_i}\)。不妨重排序列,设 \(\min\{p_i\} = p_1\),\(\min\{{p'}_i\} = {p'}_1\)。\(p_1 \mid n \Rightarrow p_1 \mid \prod_{i=1}^{m} {p'}_i^{{c'}_i}\),则必定存在一个 \({p'}_i\) 使得 \(p_1 \mid {p'}_i\)。同理,存在 \(p_j\) 使得 \({p'}_1 \mid p_j\)。因为 \({p'}_i,p_j\) 均为素数,所以可以改写为 \({p'}_i = p_1,p_j = {p'}_1\)。
观察到 \(p_1,{p'}_1\) 均为序列中最小的数,因此 \(p_1 = {p'}_1\)。将这两项移除,可以递归地证明。
- 推论
\(n\) 的正约数个数为\[\prod_{i=1}^{m} (c_i+1) \]证明:感性理解,\(n\) 的约数的唯一分解中每个质数的次数不可能超过 \(n\) 的唯一分解中该质数的次数。只要满足了这个限制,次数可以随便取,取 \(0\)(不选这个质因子)到 \(c_i\) 中的任何指数都可以,因此是 \(c_i + 1\) 种取法。根据乘法原理,把每个质数的选法乘起来。
\(n\) 的正约数和为
证明:和上面差不多,不再赘述。
常用性质
-
性质 \(1\)
\[d \mid a ,d \mid b \Rightarrow d \mid ax + by (x,y \in \mathbb{Z}) \]证明:显然。值得一提的是,\(ax + by\) 被称为 \(a,b\) 的线性组合。
-
引理 \(1\)
\(d | a\) 的充分必要条件是 \(d\) 中任意一素因子的指数不超过 \(a\) 中该素因子的次数。
这是显然的,不需要给出证明。
-
引理 \(1\) 的推论
\(\text{lcm} (a,b) \times \gcd(a,b)= a \times b\)。
证明:令 \(a,b\) 的唯一分解分别为 \(a = \prod \limits_{i=1}^{m} p_i^{c_i},b = \prod \limits_{i=1}^{m} p_i ^{d_i}\),此时对于每个 \(i\),\(c_i,d_i\) 不能同时为 \(0\)。由引理 \(1\) 可知,\(\text{lcm}(a,b),\gcd(a,b)\) 的唯一分解分别为 \(\text{lcm}(a,b) = \prod \limits_{i=1}^{m} p_i ^{\max(c_i,d_i)},\gcd(a,b) = \prod\limits_{i=1}^{m} p_i^{\min(c_i,d_i)}\)。
显然对于任意整数 \(a,b\),有 \(\max(a,b) + \min(a,b) = a+b\),因此 \(\text{lcm}(a,b) \times \gcd(a,b) = \prod\limits_{i=1}^{m} p_i ^{\max(c_i,d_i) + \min(c_i,d_i)} = \prod\limits_{i=1}^{m} p_i ^{c_i + d_i} = a \times b\)。
-
性质 \(2\)
\[d \mid a,d \mid b \Leftrightarrow d \mid \gcd(a,b) \]其中 \(\gcd(a,b)\) 是 \(a,b\) 的最大公约数。
证明:右推左很显然。因为 \(d \mid \gcd(a,b)\),又 \(a,b\) 均为 \(\gcd(a,b)\) 的倍数,因此 \(d\) 也整除 \(a,b\)。
由引理 \(1\) 及其推论,设 \(d = \prod \limits_{i=1}^{m} p_i^{e_i}\),\(d \mid a\) 即 \(\forall i,e_i \leq c_i\)。\(\forall\) 是「对于任意」符号,\(c_i\) 为 \(a\) 的唯一分解中各个素因子的次数。
同理可推得 \(\forall i,e_i \leq d_i\),其中 \(d_i\) 为 \(b\) 的唯一分解中各个素因子的次数。
两式联立,得 \(\forall i,e_i \leq \min(c_i,d_i)\),即 \(d \mid \gcd(a,b)\)。
-
性质 \(3\)
\[\gcd(a,b) = \gcd(b,a \mod b) \]证明:考虑证明一个更强的条件,即 \(\gcd(a,b)\) 与 \(\gcd(b, a\mod b)\) 的约数集合相同。
令 \(a = kb + r(0 \leq r < b)\)。
-
左推右:\(d \mid a,d \mid b \Rightarrow d \mid r\)
由性质 \(2\),上面的条件和 \(d \mid \gcd(a,b) \Rightarrow d \mid r\) 等价。\(r = a - kb\),由性质 \(1\) 可知 \(r\) 是 \(a,b\) 的一个线性组合,由已知得 \(d \mid a,d \mid b\),则 \(d \mid r\) 显然成立。
-
右推左:\(d \mid r ,d \mid b \Rightarrow d \mid a\)
\(a = kb + r\),即 \(r\) 与 \(b\) 的一个线性组合。剩下的部分和上面类似,不再证明。
-
-
欧几里得算法求 GCD
inline int gcd(int a,int b){ if(b==0) return a; return gcd(b,a%b); }
不断利用性质 \(3\),直到 \(b=0\),这个时候返回 \(a\),即上一层时的 \(b\) 即可(因为上一层的 \(b\) 造成了 \(a \mod b = 0\))。