@RiverHamster
2018-08-07T20:19:35.000000Z
字数 4147
阅读 1743
对于两个自然数,如果存在自然数,使得那么b是a的倍数,称 整除 ,能够被整除,记作
约数不会增长得很快,一般内的数都最多多个约数。
//O(nlogn)打表d(n)
for(int i=1; i<=n; ++i)
for(int j=i; j<=n; j+=i)
d[j]++;
复杂度为什么是?
时间复杂度是
括号内是调和级数,可以近似看成
注意,1不是素数,也不是合数
正常判断需要时间,代码就不写了。
埃氏筛法:
for(int i=2; i<=n; i++){
if(!np[i]){
p[++cnt] = i;
for(int j=i+i; j<=n; j+=i)
vis[j] = true;
}
}
每个数被筛了很多次,所以可以进行优化。
欧拉筛法(线性筛)
for(int i=2; i<=n; ++i){
if(!np[i]) p[++cnt] = i;
for(int j=1; i * p[j]<=n; ++j){
vis[i * p[j]] = true;
//保证p[j] 是 i*p[j]的最小质因子
if(i % p[j] == 0) break;
}
}
每个数只会被最小的质因子筛一遍,所以筛出每个素数的时间是,所以时间复杂度是
但是埃氏筛法也有好处:可以区间筛,例如筛出区间内的素数。
估算素数的个数:
每个大于1的自然数军可以写成素数的积,并且素因子按大小排列之后,写法仅有一种方式
乘法可以写成对应素因子的次数相加,除法可以写成对应素因子的次数相减。
设一个自然数为
1. 如果是素数,那么显然只能写成它本身。
2. 如果是合数,根据定义,它可以分解成几个数的积,递归证明即可
设
因为质数是不可以分解的,所以这两个表达式是等价的。
理解即可,详细的可以自行查阅
int factorize(int x, int p[]){ //每个数反复出现就会被记录多次
int cnt = 0;
for(int i=2; i*i<=x; i++){
while(x % i == 0){
p[++cnt] = i;
x / = i;
}
}
if(x > 1) p[++cnt] = x;
}
复杂度是,因为sqrt较大,所以就是
也可以在两个数组中记录质数和次数
linux中有factor
命令,bash
执行factor [INTEGER]
,输入一个数就可以进行质因数分解,与上面的函数效果相同,十分方便。
两个自然数共有的约数最大的一个,称为它们的最大公约数
利用唯一分解定理及推论,可以知道两个数的就是两个数质因数分解后素因子次数取后的数。
定义对于任何自然数,
则对于任意的自然数,满足
如果 且 ,那么(辗转相减法),那么可以表示为
int gcd_iter(int a, int b){
}
int gcd_recur(int a, int b){
if(a == 0) return b;
return gcd(b%a, a);
}
两个自然数共有的倍数最小的一个,成为它们的最小公倍数
可以发现lcm
就是两个数对应的素因子个数取max
的结果
不需要另外求,只需要用一个公式求:
对于任意整数,存在无穷多组整数对满足不定方程,其中
在求的同时,可以求出关于的不定方程的一组整数解。
考虑递归计算:假如已经计算出了的一组解,可以递推:
$
可以整理得到递推代码,参见我的博客。以下是另外一种写法。
int exgcd(int a, int b, int &x, int &y){
if(a == 0){
x = 0; y = 1;
return b;
}
//x = x0 - b/a*y0
//y = y0
int d = gcd(b%a, a, y, x);
x -= b/a*y;
return d;
}
求
对于某个素数,根据唯一分解定理及推论,对几个数进行质因数分解,得出每个质因数次数的取值范围,然后根据乘法原理取值。
可以用枚举素因子,由于每个数至多只有一个大于的质因子,特判即可。组数据可以过。
可以先筛出素数,然后再质因数分解。
设,如果不能整除,显然无解,否则把约掉。
可以把两部操作组合,组成新操作:以下表示横坐标,表示纵坐标
根据裴蜀定理,就可以得到
考虑奇偶性,分别枚举起点:
考虑这四种初始走法,然后可以用exgcd做。
对于自然数,正整数,
如果,可以记作
这样所有的自然数都能用之间的整数代表。
取模可以拓展到负数,得数依然是小于的非负整数。
可以建立一个新的数字运算体系,其中只有的整数。
两个数相加,如果超过了,就从开始往上加,想乘与相加类似
Warning! C++中的负数取模与数学不完全相同,最好加成正数,在减法和一些数论题中注意
//防止负数取模,数学中可以不加m
除法不能保证可以除尽,所以在取模意义中较为麻烦
可以不断将除数加上,直到可以除尽,但这样太慢了。
除以一个数可以转化为乘它的倒数。所以我们可以试着找一个乘法逆元,即满足的整数,没有逆元。
当且仅当为素数时,每个正整数都有唯一的乘法逆元,因此大部分OI题中模数都是素数。
对于任意的质数和正整数,都有
易证可以取遍之间的所有数。
反证法:假设这些数会出现重复。
那么一定有,那么,不成立。
于是
然后两边同时除以,得证。
对于正整数,则
有了费马小定理,可以快速求出乘法逆元
由于,所以快速幂求即可,
也可以用求逆元,求解 即可。
有若干个人列队,三个一排多个,五个一排多个,七个一排多个,求人数至少是多少?
本质上是解一元线性同余方程组
尝试构造出这个解。
设
只有在模时不为零,模其他的都为零我们想要在模时得到,所以需要乘上,其中是在模意义下的乘法逆元。
最终得到,在对取模,就得到了最小自然数解,加上就是通解。
为什么加起来就可以?
因为其他的模就变成0了,只剩下当前这个
M太大怎么办?
...
给定一个有向图,每条边代表连接的两个点上的数字需要满足某个比例,求是否有一种合法的方案给每个点分配一个数字。
dfs
或并查集
做,依次分配数字。int
会溢出,用double
会爆精度我们需要取多个,分别做一遍,可以大几率通过。
也可以用取对数加减或质因数分解等奇怪方法做。
把洗牌的过程反过来,会发现假设一张牌洗牌后是,洗牌前是,证明较为复杂,需要用到置换矩阵。
解方程
求出的逆元即可,由于不一定是素数,所以求逆元只能用或欧拉函数