素数筛
Eratosthenes筛
找出小于n的所有素数,方法是这样的:
首先,从2开始遍历每个小于n的数i,对于i的倍数,我们知道肯定不是素数,所以标记它为合数。
然后优化,假设j<i,那么i的j倍一定在遍历j的倍数的时候就已经标记过了,那么我们在遍历i的倍数的时候,直接从i的i倍开始。
一直遍历到i的j倍不再小于等于n结束。
时间复杂度为O(nloglogn),结论怎么来的笔者不知道,猜测和自然数中素数的密度有关。
1 |
|
线性筛
Eratosthenes筛仍然会标记重复的合数,如标记12时,会在扫描2,扫描3的时候各标记一遍。
为此,再次优化算法,得到了时间复杂度为O(N)的线性筛。
1、以此考虑2~N的每个数i
2、若vi=0,说明i是质数,将其保留下来
3、扫描不大于vi的每个质数p,令vi×p=p
这样,每个合数只会被其最小质因子筛一次。
1 |
|
CH3101_阶乘分解
描述
给定整数 N(1≤N≤10^6),试把阶乘N!分解质因数,按照算术基本定理的形式输出分解结果中的p_i和c_i即可。
思路
思路1(自己想的):计算出1~1000的所有质数,然后设1~1000000这么大的数组,下标和值相等,用1~100000挨个除这些质数并统计个数。然后检查得到的商,这时商如果不是1,必定是大于1000的质数,所以再排序,统计1000以上的质数个数。复杂度O(N^1.5)
思路2(蓝书):因为N!中含有某个质因子的数目就是1~N这些数中含有这个质因子的数目之和。因此,N/p下取整就是所有至少含一个p的整数数目,N/p/p再下取整就是至少含两个p的整数数目。要计算所有p的数目,应计算N/p+N/p/p+……。因为1~N连乘必然含有小于等于N的所有素数,所以要求出1~N的所有素数才行。复杂度O(NlogN)
因为题目数据较弱,我的思路也通过了。但由于此题对于阶乘分解的方法具有普遍意义,我按蓝书上的思路又重写了代码。
解决
思路1
1 |
|
思路2
1 |
|
POJ2689_Prime Distance
描述
给定两个数l和u,要求找出l到u之间距离最小和最大的相邻素数。
范围
l和u都是int型正整数,u-l小于等于1e6
思路
由于u-l比较小,所以可以筛出根号u以内的所有素数,用它们挨个检验u到l之间的数。因为任何合数都会至少有一个小于等于它算术平方根的因子。所以这样检测不到的一定是素数。
解决
1 |
|
反思和坑
计算的中间结果会超过int的最大范围,最好的办法是全部使用long long。
注意l、u的取值范围包括1,而1是不算素数的。
最后发现一直错误的原因是自作聪明的剪枝:
检查l到u之间的数有没有质因子时,没有把2~50000中所有的素数都检查一遍。
约数
试除法
求某个数的全部约数,只需要扫描1~N^(1/2)中所有数,尝试该数能否整除N即可。
1 |
|
试除法的推论
任何正整数N的因数个数不会超过2N^(1/2)
倍数法
求1~N的全部正整数各自的全部因数。可以考虑对1~N的所有正整数遍历其倍数,复杂度为NlogN
1 |
|
倍数法的推论
1~N的全部正整数的因子个数和大约为NlogN
BZOJ1053_反素数
描述
设一个数x的约数个数为h(x),那么如果任取y < x,有h(y) < h(x),称x为反素数。
1,2,4,6都是反素数
现给出N,求N以内(包括N)的最大反素数
范围
N <= 2e9
思路
这个题应该是我做不出来的,因为涉及到了一些推导
首先,如果两个数因数个数相同,那么反素数只可能是小的那个
其次,比2e9小的数质因子不会超过9个,这是因为前10个质数的乘积为6,469,693,230,已经大于2e9了
最后,N以内的反素数必定表示为(2^p1)×(3^p2)×(5^p3)×(7^p4)×(11^p5)×(13^p6)×(17^p7)×(19^p8)×(23^p9)×(29^p10),这其中必定有p1到p10递减
经由以上推导,我们就可以使用一个简单的dfs来确定p1到p10的具体数目
这里还用到了一个东西,如果x的算术基本定理分解式为:
x = (p1^a1)×(p2^a2)×……×(pn^an)
那么x的因数个数是(a1+1)×(a2+1)×……×(an+1),这结论稍微一动脑子就能想出来
这可以用在dfs里
解决
1 |
|
BZOJ1257_余数之和
描述
给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值,其中k mod i表示k除以i的余数。
例如j(5, 3)=3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5=0+1+0+3+3=7
范围
1 <= n, k <= 1e9
思路
肯定不能直接算,但怎么简化呢?
可以考虑n > k时的情况,此时余数必为k,总和为k × (n - k),接下来集中解决n <= k时的情况
令k除以n,考虑:
1 | n = k 余数是0, 商是1 |
余数逐个增大1,这种情况在什么时候停止呢?在n小于k的一半的时候,再之后,余数又会逐个增大2,以此类推
1 | n = k / 2 余数是0,商是2 |
可见,余数构成等差数列,直到商重置时为止
这样,我们可以计算商为1时的等差数列余数和,然后商为2,一直到商为k
但这样又导致复杂度变成了O(k),还是太大
我们观察商比较大时的情况,举个例子,假设k = 36,商为9,此时余数可能有0一种,然后商为10,没有可能,商为11,又没有可能
可见,商比较大时,可能有余数的区间更为稀疏
因此,我们只计算商为1到1000的区间,然后剩下的直接用取余式求出k除以1到k除以(k/1000)的余数即可
解决
1 |
|
欧拉函数
POJ3090_Visible Lattice Points
描述
从平面直角坐标系上,如果从某个点A出发到另一个点B连成的直线中间没有第三个点,那么可以说在A点能看到B点
现在,给出x和y坐标为在[0,N]之间整数的点,求从原点看去,有多少个可见的点
数据范围
1 < N < 1001
思路
首先,我们知道有三条特殊的线,x轴,y轴和y=x,这三条线上所有点都会被最前面的挡住
然后,如何判断某个点是否被前面的点挡住了呢?很简单,如果这个点的x和y坐标互质,就一定不会有点挡在这个点的前面
问题转换成了求多少个x和y坐标互质的点
然后,我们可以固定x坐标为p,那么显然这个数字就是p的欧拉函数phi(p)
因此答案是∑phi(n) + 3
解决
1 | #include <cstdio> |
POJ3696_The Luckiest number
描述
给出一个数L,寻找最短的由8组成的数字,使该数字是L的倍数
输出这个数字有几个8
数据范围
L < 2e9
思路
可以把由8构成的数看作8(10x-1)/9。问题转换成了求最小的x值
形如10x-1的数显然是不含有因子2和5的
所以8(10x-1)若能被L整除,L只能含有不超过3个因子2,并且不含有因子5
我们去掉L中的因子2后,问题转为10x-1能被9L整除
此时L与10互质,根据欧拉定理,我们直到x是phi(9L)的因数
那么,我们只需要列举因数,利用快速幂找出最小的符合条件的数即可
解决
1 | #include <cstdio> |
欧几里得算法和扩展
CH3301_同余方程
描述
求关于 x的同余方程 ax ≡ 1(mod b) 的最小正整数解。
数据范围
对于 40% 的数据 2 ≤b≤1,000
对于 60% 的数据 2 ≤b≤50,000,000
对于 100%的数据 2 ≤a, b≤2,000,000,000
思路
先用拓展欧几里得算法求出通解
然后对b取模
解决
1 | #include <cstdio> |
CH3201_Hankson的趣味题
描述
已知正整数a0,a1,b0,b1,设某未知正整数x满足:
1 | 1、 x和a0的最大公约数是a1; |
Hankson的“逆问题”就是求出满足条件的正整数x。但稍加思索之后,他发现这样的x并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的x的个数。请你帮助他编程求解这个问题。
保证a0是a1的倍数,b1是b0的倍数
输入格式和范围
先输入一个整数n表示问题的个数
然后有n行,每行四个数a0,a1,b0,b1
n不大于2000,每次的四个整数不大于2e9
思路
把a0,a1,b0,b1都分解质因数
从题目条件给的倍数关系,可以知道,凡是a1有的质因数,a0一定也有,并且数目只多不少
举例说明:假设a1分解出了两个2,那么a0作为a1的倍数,也至少能分解出两个2
此外,作为a1和x的最大公因数,a0含有的质因子个数为a1和x含有质因子个数的最小值
举例说明:假设a1含有两个2,x含有一个2,那么a0作为最大公因数,一定含有一个2
我们遍历a0的质因数p,假设a0含有m0个p,a1含有m1个p,那么a1中的含p情况有两种:
1 | 1.m1 = m0,说明a0是含p较少的那个,所以x至少也要含有m0个p |
这样,我们可以把x的含各种质因数的情况列出来
然后,对于b0和b1,有类似的结论
为了加速,我们把2e5以内的质数先筛出来
解决
1 |
|