Day 6的第二部分,数论
数论是纯粹数学的分支之一,主要研究整数的性质
1.一些符号:
a mod b 代表a除以b得到的余数
a|b a是b的约数
floor(x) 代表x的下取整,即小于等于x的最大整数,也可以认为是直接舍去小数部分
(这个应该是一个符号,但我不知道怎么打出来。。下面那个ceil也是)
ceil(x) 代表x的上取整,即大于等于x的最小整数,也可以认为是直接舍去小数部分再+1.
gcd(a,b) 表示a与b的最大公约数
lcm(a,b) 表示a与b的最小公倍数
累加符号∑ 累乘符号∏
logab代表以a为底的b的对数,ln表示以e为底的对数,lg代表以10为底的对数
2.质数
也称素数,是质因子只有1和它本身的数。
与质数相对的是合数,合数在因数拆分时可以拆分成至少两对其他数的乘积。
特殊规定,1既不是质数也不是合数。
可以证明,素数是无限多个的,证明从略。
3.唯一分解定理
算术基本定理之一。
定理是这样描述的:对于每一个大于1的自然数都可以写成质数的积,而这些因子按照大小排列后,只有一种结果(写法只有一种)。
我并不会证。。。我太弱了
这个定理是分解质因数的基础。它告诉我们可以用质数乘积的形式处理一些大数字,处理一类和约数有关的问题。
显然,一个数n最多有一个大于√n的质因子。
4.筛法
用来快速求一个数是不是质数的算法
通常有两种筛法:埃氏筛和欧拉筛
4.1埃氏筛法
核心思想:从2到n枚举,当找到一个质数时,枚举它的所有倍数,这些倍数绝对不可能是质数。
int cnt; int prime[maxn]; bool vis[maxn]; void Eratosthenes(int n){ for (int i=2;i<=n;i++) if (!vis[i]){ prime[++cnt] = i; for (int j = i*2;j<=n;j+=i) vis[j] = 1; } }
时间复杂度是O(nloglogn),证明从略。
4.2欧拉筛法
核心思想是通过让每一个数只会被它最小的质因子筛到,从而每个数只会被筛一次,时间复杂度O(n)。
对于任意一个合数,我们可以拆成最小质因子*某数i的形式,我们枚举这个数i,然后再枚举出所有筛的质数。
当我们枚举的质数可以整除i时,如果再往大里枚举,枚举的质数就不可能是最小质数了。这时就可以终止循环,继续枚举下一个i。
int cnt; int prime[maxn]; bool vis[maxn]; void Eular(int n){ for (int i=2;i<=n;i++){ if (!vis[i]) prime[++cnt] = i; for (int j=1;j<=cnt && prime[j]*i<=n;++j){ vis[prime[j]*i] = 1; if (i % prime[j] == 0) break; } } }
5.取模
我想您应该早已熟知余数的定义。
a % b(就是a mod b)得到的结果是a除以b得到的余数。
性质:a %b = floor(a/b)
一些题目要求对答案取模,如果这个答案是负的,应该加上模数而不是除。
取模的性质:
(a±b) % p = ((a % p) ± b) % p
(a*b) %p=(a % p) * (b % p)
6.最大公约数
辗转相除。
int gcd(int a, int b){ if (!b) return a; return gcd(b, a % b); }
7.同余
这里引入一个新符号,a≡b(mod n)表示a与b在模n意义下的余数相同,称为a与b同余。
同余的性质:
1.反身性 a≡a (mod m)
2.对称性 若a≡b(mod m),则b≡a (mod m)
3.传递性 若a≡b (mod m),b≡c (mod m),则a≡c (mod m)
4.同余式相加 若a≡b (mod m),c≡d(mod m),则a+c≡b+d (mod m)(或换成-)
5.同余式相乘 若a≡b (mod m),c≡d(mod m),则ac≡bd (mod m)
7.1逆元
如果 ax≡1(mod n),那么x为a关于模n意义下的逆元。
逆元一般用inv简称。
当gcd(a,n) = 1时x为一唯一值,否则不存在,证明从略。
其实可以这么想,x为模n意义下的倒数,也就是1/a,我们可以类比实数意义下的倒数在模n意义下a乘以b的逆元,相当于模n意义下的a/b。
求逆元的方法,接下来讲。
8.扩展欧几里得算法
(借鉴了gty哥哥课件的思路)
简称扩欧,exgcd。
用来求解形似ax+by = gcd(a,b)一类方程的解。
这里的x和y不一定是正整数,有可能是负数或0.
比如说我举个例子,求一直线ax+by+c = 0上有多少个整数点(x,y)满足x∈[x1,x2],y∈[y1,y2]
边界情况:
当b=0时,gcd(a,b)=a,x=1,y=0
假设 ax1 + by1= gcd(a,b),bx2 + (a % b)y2= gcd(b,a % b)
由gcd的意义,知gcd(a,b) = gcd(b,a % b),那么有ax1 + by1 = bx2+ (a % b)y2;
也就是说ax1 + by1 = bx2 + (a - [a / b] * b)y2 = ay2 + bx2 - [a / b] * by2;
也就是说ax1 + by1 == ay2+ b(x2 - [a / b] *y2);
那么,x1 = y2; y1 = x2 - [a / b] * y2;
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2。我们可以通过不断递归调用求解。
void exgcd(int a, int b, int &d, int &x, int &y){ if (!b) { d = a; x = 1; y = 0; } else { exgcd(b, a % b, d, y, x); y -= (a / b) * x; } }
我们这样只能得出一组解,其他解呢?如果我们现在有解(x1,y1),任取另外一组解(x2,y2),则有ax1 + by1 = ax2 + by2 = gcd(a, b),变形可以得到a(x1 – x2) = b(y2 – y1),两边同时除以gcd(a, b),得到a’(x1 – x2) = b’(y2 – y1),因为(a’,b’)=1,所以(x1-x2)一定是b’的倍数,取x1-x2=kb’,得y2-y1=ka’。
所以我们有以下结论:
对方程ax+by+c=0,一组整数解为(x0,y0),则它的任意整数解可以写成(x0+kb’,y0-ka’),其中a’=a/gcd(a, b),b’=b/gcd(a, b)
关于ax+by=c有没有解,我们有这么一个结论:
对于方程ax+by=c(a,b,c均为整数),如果c为gcd(a,b)的倍数,则方程有整数解,反之无整数解。因为a和b都是gcd(a,b)的倍数,所以ax+by一定也是gcd(a,b)的倍数,如果c不是gcd(a,b)的倍数,一定无解。
那刚才那道题怎么做呢?
方程变形为ax+by = -c,看一下-c是不是gcd(a,b)的倍数,然后用exgcd求一下ax+by = gcd(a,b)的解,记为(x0,y0)。
等式两边同乘(-c)/gcd(a,b),就有ax0‘+by0‘ = -c
用刚才的结论,求出使x = x0 + kb‘落在区间[x1,x2]内的k的范围和使y = y0-ka‘落在区间[y1,y2]内的k的范围,取交集就是答案。
9.费马小定理
如果p是质数,并且gcd(a,p) = 1,那么 a^(p-1) ≡ 1 (mod p)
由这个式子我们可以知道,a关于p的逆元就是a^(p-2) % p,用快速幂就好,证明从略。
10.快速幂
快速幂用了一个倍增的思想,我把它放在Part 3.
11.排列组合
11.1排列
排列数指的是从n个数中任取m个元素排成有顺序的一列,这一列称为一个排列,所有排列总数称为排列数。
生成排列数:DFS 或 next_permutation
从n个元素里取m个成为排列记为A(n,m) (原谅打不出来)
A(n,m) = n * (n-1) * (n-2) * (n-3) * ... * (n-m+1) = n!/m!(n-m)!
11.2组合
组合数指的是从n个数中任取m个 元素设为一组,叫做一个组合,这样所有的组合的数量就是组合数。
可以发现,排列要求元素带有顺序,而组合则不考虑顺序。
公式是C(n,m) = n * (n-1) * (n-2) * (n-3) * ... * (n-m+1)/m! = n!/m!(n-m)!
组合的性质:
C(n,m) = C(n-1,m-1) + C(n-1,m)
C(n,m) = C(n,n-m)
C(n,m) = C(n,m-1)*(n-m+1)/m (m>0)
由性质3可递推某一n确定下的组合数
其实排列组合这一块有很多内容,然而。。。
我觉得我讲得再多也没高中数学选修2-3讲得明白。。。。
所以,如果有没提到的地方,请参见高中数学选修2-3。。