判定
0-sqrt(n)内看能不能整除。
证明:反证法
筛选
(1)最最朴素想法:每个都判定一下,时间复杂度O(nsqrt(n))
(2)eratosthenes法:接近O(n),比其大一点
对于比n小的质数x 从小到大枚举,x,2x,3x…等都显然不是质数。依次标记。
优化:x…,(x-1)显然已经被前边的筛没了,故每次从x*x开始
(3)线性筛法:O(n)
对于合数例如12=2x6=3x4会被筛2次。
若让每个数只被他的最小质因子筛掉,则显然每个数就只会被处理一次了。这是因为显然一个数不可能有两个最小质因子。
因此理解如下:
遍历每个数,
若没有被标记过,质数数组加一个。
我们这样理解:我们只需要对于每个质数,找到2-n中以这个质数作为最小质因子的数(当然不包括自己),把他去掉即可。
由于我们已经有了一层倍数i的循环了。-(当然这个原始目的是为了吧所有数据判断一遍,但功能一样不妨合并。)
即不妨我们这样想:
找到了一个质数p,则p*i肯定全是合数。
若让p作为最小质因子筛的话,i满足 i的所有除了1以外的质因子是大于p的。
因此,遍历i,如果其没被标记过,则其是质数。
然后(从小到大)遍历已知的质数,只带i%p==0.这个时候,如果继续往下遍历则可能出现不满足题意的p
int primes[N], cnt;
bool st[N];
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;//如果i没有被标记过,其为质数
for (int j = 0; primes[j] <= n / i; j ++ )//一定要有等号
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
(4)当题目出现求[L,R]内的所有质数时,不妨先用线性筛法求sqrt(R)内的质数,然后再用eratosthenes法算,这样时间复杂度要小于直接线性筛法
质数距离
知识点
1、上取整与下取整的关系:
⌈
a
b
⌉
\lceil \frac{a}{b} \rceil
⌈ba⌉=
⌊
a
+
b
−
1
b
⌋
\lfloor \frac{a+b-1}{b} \rfloor
⌊ba+b−1⌋
理解:
不妨设 a=kb+r(0<=r<=b-1)
左右同时正除以b,推出
⌊
a
b
⌋
\lfloor \frac{a}{b} \rfloor
⌊ba⌋=k
而a+b-1=(k+1)b+r-1(0<=r<=b-1)
左右同时正除以b,
若r=0,推出
⌊
a
+
b
−
1
b
⌋
\lfloor \frac{a+b-1}{b} \rfloor
⌊ba+b−1⌋=k
若r>0,推出
⌊
a
+
b
−
1
b
⌋
\lfloor \frac{a+b-1}{b} \rfloor
⌊ba+b−1⌋=k+1
而这恰好就是上取整
2、如何找到第一个>=l的第一个p的倍数呢?
x=
⌈
l
p
⌉
∗
p
\lceil \frac{l}{p} \rceil *p
⌈pl⌉∗p
3、不超过n的质数大概有
n
l
n
n
\frac{n}{ln n}
lnnn个
做法
线性筛法是O(n)级别的,大概可处理10^9个的操作。
显然能直接从1-R进行线性筛法。
因此考虑二次筛法,先计算根号n内的数,然后再进行一次筛选。
注解
ly20 同学在y总的代码 问的3个很高质量的问题,也是很细节的地方,感谢ly20 同学
(1)每个质数是2~50000中的数,为啥 LL p = primes[i]; 这里的p的LL啊, int 不就够了吗?
(2)for (LL j = max(p * 2, (l + p - 1) / p * p); j <= r; j += p) 这里的 j 是小于等于 r 的,而 r 的取值范围是小于2 ^ 31,也是在int 范围内啊, 这里为啥用LL啊?
(3) 我知道这里是复用st数组,但是在用之前都初始化为0了啊,为什么init(50000); 放在while (cin >> l >> r) 的外面(也就是最前面)代码不行啊?
回答:
(1)LL p = primes[i],这里p用LL是因为如果p也是用int类型,本身l也是用int类型,如果l取得足够大,下面的l + p - 1会有可能直接爆int变成负数
(2)这里的 j 是小于等于 r 的,而 r 的取值范围是小于2 ^ 31,这里确实是这样,可是这个循环跳出的条件是j <= r,也就是说如果r是最大的int,那么当j += p,要超过最大的int的时候需要比它还大才能跳出循环,因此直接爆int变成负数,然后j <= r依然成立,会一直死循环下去
(3)放在最前面也是可以的,y总的代码中判断从[1,50000]中谁是质数和在区间[L,R]中谁是质数直接复用st[]数组,就不用再开一个数组去存了,也可以把init()放在前面,用一个专门的数组去记录区间[L,R]中谁是质数
时间复杂度 O(n)
作者:小呆呆
链接:https://www.acwing.com/solution/acwing/content/11586/
来源:AcWing
代码
#include<iostream>
#include<cstring>
using namespace std;
const int N=1000010;
typedef long long ll;
int st[N],primes[N],cnt;
ll L,R;
void init(int n)
{
memset(st,0,sizeof(st));
cnt=0;
for(int i=2;i<=n;i++)
{
if(!st[i])primes[cnt++]=i;
for(int j=0;primes[j]*i<=n;j++)
{
st[i*primes[j]]=true;
if(i%primes[j]==0)break;
}
}
}
int main()
{
while(cin>>L>>R)
{
init(50000);
memset(st,0,sizeof(st));
for(int i=0;i<cnt;i++)
{
ll p=primes[i];
for(ll j=max(2*p,((L+p-1)/p)*p);j<=R;j+=p)
{
st[j-L]=true;
}
}
cnt = 0;
for (int i = 0; i <= R - L; i ++ )
if (!st[i] && i + L >= 2)
primes[cnt ++ ] = i + L;
if (cnt < 2) puts("There are no adjacent primes.");
else
{
int minp = 0, maxp = 0;
for (int i = 0; i + 1 < cnt; i ++ )
{
int d = primes[i + 1] - primes[i];
if (d < primes[minp + 1] - primes[minp]) minp = i;
if (d > primes[maxp + 1] - primes[maxp]) maxp = i;
}
printf("%d,%d are closest, %d,%d are most distant.\n",
primes[minp], primes[minp + 1],
primes[maxp], primes[maxp + 1]);
}
}
}
阶乘分解
做法
直接做是O(n*sqrt(n))复杂度过高;
转换思路,1-n所有的数质因数分解后一定不可能大于n;
然后只需要统计含有质因子p的个数之和即可。
首先先找到所有的质数,
对于每个质数p,
至少包含一个质因子p的个数有
⌊
n
p
⌋
\lfloor{\frac{n}{p}}\rfloor
⌊pn⌋
至少包含两个质因子p的个数有
⌊
n
p
2
⌋
\lfloor{\frac{n}{p^2}}\rfloor
⌊p2n⌋
以此类推。
由于我们关心的是p的个数,而恰好两个包含两个p的数,其中有部分已经被前边统计过了。
实际上最后的p的个数即为
∑
⌊
n
p
i
⌋
\sum{\lfloor{\frac{n}{p^i}}\rfloor}
∑⌊pin⌋
代码
#include<iostream>
using namespace std;
const int N=1000000;
int cnt;
int primes[N],st[N];
void get_primes(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])primes[cnt++]=i;
for(int j=0;primes[j]*i<=n;j++)
{
st[i*primes[j]]=true;
if(i%primes[j]==0)break;
}
}
}
int main()
{
int n;
cin>>n;
get_primes(n);
for(int j=0;j<cnt;j++)
{
int p=primes[j];
int t=n,s=0;
while(t)t=t/p,s+=t;
cout<<p<<" "<<s<<endl;
}
}