[关闭]
@RiverHamster 2018-08-07T20:19:35.000000Z 字数 4147 阅读 1743

Day 7:提高数论

Part1 基本



倍数与约数

倍数

对于两个自然数,如果存在自然数,使得那么b是a的倍数,称 整除 能够被整除,记作

约数

约数不会增长得很快,一般内的数都最多多个约数。

  1. //O(nlogn)打表d(n)
  2. for(int i=1; i<=n; ++i)
  3. for(int j=i; j<=n; j+=i)
  4. d[j]++;

复杂度为什么是
时间复杂度是
括号内是调和级数,可以近似看成

素数和唯一分解定理

素数与合数

注意,1不是素数,也不是合数

正常判断需要时间,代码就不写了。

埃氏筛法:

  1. for(int i=2; i<=n; i++){
  2. if(!np[i]){
  3. p[++cnt] = i;
  4. for(int j=i+i; j<=n; j+=i)
  5. vis[j] = true;
  6. }
  7. }

每个数被筛了很多次,所以可以进行优化。

欧拉筛法(线性筛)

  1. for(int i=2; i<=n; ++i){
  2. if(!np[i]) p[++cnt] = i;
  3. for(int j=1; i * p[j]<=n; ++j){
  4. vis[i * p[j]] = true;
  5. //保证p[j] 是 i*p[j]的最小质因子
  6. if(i % p[j] == 0) break;
  7. }
  8. }

每个数只会被最小的质因子筛一遍,所以筛出每个素数的时间是,所以时间复杂度是

但是埃氏筛法也有好处:可以区间筛,例如筛出区间内的素数。

估算素数的个数:

唯一分解定理(算术基本定理)

对唯一分解定理的证明

存在性

设一个自然数为
1. 如果是素数,那么显然只能写成它本身。
2. 如果是合数,根据定义,它可以分解成几个数的积,递归证明即可

唯一性




因为质数是不可以分解的,所以这两个表达式是等价的。

理解即可,详细的可以自行查阅

  1. int factorize(int x, int p[]){ //每个数反复出现就会被记录多次
  2. int cnt = 0;
  3. for(int i=2; i*i<=x; i++){
  4. while(x % i == 0){
  5. p[++cnt] = i;
  6. x / = i;
  7. }
  8. }
  9. if(x > 1) p[++cnt] = x;
  10. }

复杂度是,因为sqrt较大,所以就是

也可以在两个数组中记录质数和次数

linux中有factor命令,bash执行factor [INTEGER],输入一个数就可以进行质因数分解,与上面的函数效果相同,十分方便。

gcd & lcm

最大公约数gcd

两个自然数共有的约数最大的一个,称为它们的最大公约数

利用唯一分解定理及推论,可以知道两个数的就是两个数质因数分解后素因子次数取后的数。

辗转相除法(欧几里得算法)

定义对于任何自然数

则对于任意的自然数,满足

如果,那么(辗转相减法),那么可以表示为

  1. int gcd_iter(int a, int b){
  2. }
  3. int gcd_recur(int a, int b){
  4. if(a == 0) return b;
  5. return gcd(b%a, a);
  6. }

最小公倍数lcm

两个自然数共有的倍数最小的一个,成为它们的最小公倍数

可以发现lcm就是两个数对应的素因子个数取max的结果

不需要另外求,只需要用一个公式求:

扩展欧几里得exgcd

定理:裴蜀定理

对于任意整数,存在无穷多组整数对满足不定方程,其中

在求的同时,可以求出关于的不定方程的一组整数解。

考虑递归计算:假如已经计算出了的一组解,可以递推:
$

可以整理得到递推代码,参见我的博客。以下是另外一种写法。

  1. int exgcd(int a, int b, int &x, int &y){
  2. if(a == 0){
  3. x = 0; y = 1;
  4. return b;
  5. }
  6. //x = x0 - b/a*y0
  7. //y = y0
  8. int d = gcd(b%a, a, y, x);
  9. x -= b/a*y;
  10. return d;
  11. }

Part1::Problems

P1403 约数研究

  1. 暴力统计
  2. 约数个数打表
  3. 转化成倍数问题,套用倍数个数公式,

P1072 Hankson的趣味题

对于某个素数,根据唯一分解定理及推论,对几个数进行质因数分解,得出每个质因数次数的取值范围,然后根据乘法原理取值。

可以用枚举素因子,由于每个数至多只有一个大于的质因子,特判即可。组数据可以过。

可以先筛出素数,然后再质因数分解。

P2520 向量

,如果不能整除,显然无解,否则把约掉。

可以把两部操作组合,组成新操作:以下表示横坐标,表示纵坐标

根据裴蜀定理,就可以得到

考虑奇偶性,分别枚举起点:

考虑这四种初始走法,然后可以用exgcd做。

Part2 提高



对于自然数,正整数

取模意义下的数和运算

如果,可以记作

这样所有的自然数都能用之间的整数代表。

取模可以拓展到负数,得数依然是小于的非负整数。

可以建立一个新的数字运算体系,其中只有的整数。

两个数相加,如果超过了,就从开始往上加,想乘与相加类似

Warning! C++中的负数取模与数学不完全相同,最好加成正数,在减法和一些数论题中注意


//防止负数取模,数学中可以不加m

除法不能保证可以除尽,所以在取模意义中较为麻烦

可以不断将除数加上,直到可以除尽,但这样太慢了。

除以一个数可以转化为乘它的倒数。所以我们可以试着找一个乘法逆元,即满足的整数没有逆元。

当且仅当为素数时,每个正整数都有唯一的乘法逆元,因此大部分OI题中模数都是素数。

费马小定理

对于任意的质数和正整数,都有

易证可以取遍之间的所有数。

反证法:假设这些数会出现重复。

那么一定有,那么,不成立。

于是

然后两边同时除以,得证。

欧拉定理(数论)

对于正整数,则

有了费马小定理,可以快速求出乘法逆元

由于,所以快速幂求即可,

也可以用求逆元,求解 即可。

中国剩余定理CRT

有若干个人列队,三个一排多个,五个一排多个,七个一排多个,求人数至少是多少?

本质上是解一元线性同余方程组





两两互素时一定有解

尝试构造出这个解。

只有在模时不为零,模其他的都为零我们想要在模时得到,所以需要乘上,其中在模意义下的乘法逆元。

最终得到,在对取模,就得到了最小自然数解,加上就是通解。

为什么加起来就可以?

因为其他的模就变成0了,只剩下当前这个

M太大怎么办?

...

Part2::Problems

P4079 齿轮

给定一个有向图,每条边代表连接的两个点上的数字需要满足某个比例,求是否有一种合法的方案给每个点分配一个数字。

P2054 洗牌

求出的逆元即可,由于不一定是素数,所以求逆元只能用或欧拉函数

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注