写在前面
真的没啥可看的,就当我是给一万年后我校学 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 imes 2 imes ... imes n)。
( ext{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 imes 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 mid a)。例如 (114514 mid 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(nsqrt 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) 的推论
( ext{lcm} (a,b) imes gcd(a,b)= a imes 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) 可知,( ext{lcm}(a,b),gcd(a,b)) 的唯一分解分别为 ( ext{lcm}(a,b) = prod limits_{i=1}^{m} p_i ^{max(c_i,d_i)},gcd(a,b) = prodlimits_{i=1}^{m} p_i^{min(c_i,d_i)})。
显然对于任意整数 (a,b),有 (max(a,b) + min(a,b) = a+b),因此 ( ext{lcm}(a,b) imes gcd(a,b) = prodlimits_{i=1}^{m} p_i ^{max(c_i,d_i) + min(c_i,d_i)} = prodlimits_{i=1}^{m} p_i ^{c_i + d_i} = a imes 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, amod 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))。