衔接上篇点击跳转
因为长度问题,一直发不出去,所以拆开来完成。
中国剩余定理
看了好几个博客,推导的式子都是错误的,额,越看越难受。最后在b站找到了一个讲解视频,之前就看到过,后来又忘记怎么算了,写下来记录一下。
栗子: 三三数余二,五五数余三,七七数余二,问数为多少。
翻译:
k
%
3
=
2
k\%3=2
k%3=2
k
%
5
=
3
k\%5=3
k%5=3
k
%
7
=
2
k\%7=2
k%7=2
问k最小为多少。
套用中国剩余定理的公式:
x
≡
1
(
m
o
d
3
)
x \equiv 1 (mod \ 3)
x≡1(mod 3)
x
≡
0
(
m
o
d
5
)
x \equiv 0 (mod \ 5)
x≡0(mod 5)
x
≡
0
(
m
o
d
7
)
x \equiv 0 (mod \ 7)
x≡0(mod 7)
x
≡
0
(
m
o
d
3
)
x \equiv 0 (mod \ 3)
x≡0(mod 3)
x
≡
1
(
m
o
d
5
)
x \equiv 1 (mod \ 5)
x≡1(mod 5)
x
≡
0
(
m
o
d
7
)
x \equiv 0 (mod \ 7)
x≡0(mod 7)
x
≡
0
(
m
o
d
3
)
x \equiv 0 (mod \ 3)
x≡0(mod 3)
x
≡
0
(
m
o
d
5
)
x \equiv 0 (mod \ 5)
x≡0(mod 5)
x
≡
1
(
m
o
d
7
)
x \equiv 1 (mod \ 7)
x≡1(mod 7)
由三组式子分别计算出
(
5
⋅
7
⋅
x
)
%
3
=
=
0
(5·7·x)\%3==0
(5⋅7⋅x)%3==0 的x最小值,其它两组同理。得到三个最小值为70、21、15。
(
35
⋅
2
⋅
2
+
21
⋅
1
⋅
3
+
15
⋅
1
⋅
2
)
m
o
d
(
3
⋅
5
⋅
7
)
(35·2·2+21·1·3+15·1·2) \ mod \ (3·5·7)
(35⋅2⋅2+21⋅1⋅3+15⋅1⋅2) mod (3⋅5⋅7)最终的最小值为23。
又看了巨佬的博客,写的超好。
在最后一步确定每个式子中的x的值时,采用了拓展欧几里得求解。(exgcd)
最后在根据公式把每个部分和在一起得到结果。(中国剩余定理)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=105;
ll m[maxn],a[maxn],lcm=1;
int n;
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x=1;
y=0;
return a;
}
ll res=exgcd(b,a%b,x,y),temp=x;
x=y;
y=temp-(a/b)*y;
return res;
}
ll China(){
ll d,x,y,res=0;
for(int i=1;i<=n;i++) lcm=lcm*m[i];
for(int i=1;i<=n;i++){
ll kl=lcm/m[i];
d=exgcd(kl,m[i],x,y);
x=(x%m[i]+m[i])%m[i];
res=(res+a[i]*x*kl)%lcm;
}
return res;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%lld%lld",&m[i],&a[i]);
}
printf("%lld",China());
//system("pause");
return 0;
}
若所给的质数不互质,用扩展中国剩余定理(EX_CRT),解决模数可不互质的同余方程组。
替换掉上一段代码中的China()函数即可。
ll China(){
ll M=m[1],A=a[1],t,d,x,y;
for(int i=2;i<=n;i++){
d=exgcd(M,m[i],x,y);
if((a[i]-A)%d) return -1;
x*=(a[i]-A)/d;
t=m[i]/d;
x=(x%t+t)%t;
A=M*x+A;
M=M/d*m[i];
A%=M;
}
A=(A%M+M)%M;
return A;
}
子段求和
可能数据弱吧,用前缀和给过掉了。
5e5*1e9的数据不知道会不会爆掉。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50050;
int n,q;
ll temp;
ll b[maxn];
int index,len;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>temp;
b[i]=b[i-1]+temp;
}
cin>>q;
while(q--){
cin>>index>>len;
cout<<b[index+len-1]-b[index-1]<<endl;
}
//system("pause");
return 0;
}
树状数组做法:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50010;
ll a[maxn],b[maxn];
int n,q;
int index,len;
int lowbit(int x){
return x&-x;
}
void update(int i,int x){
while(i<=n){
b[i]+=x;
i+=lowbit(i);
}
}
ll sum(int x){
ll res=0;
while(x>=1){
res+=b[x];
x-=lowbit(x);
}
return res;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
update(i,a[i]);
}
cin>>q;
while(q--){
cin>>index>>len;
int l=index,r=index+len-1;
cout<<sum(r)-sum(l-1)<<endl;
}
//system("pause");
return 0;
}
线段树做法:
(带懒惰标记的线段树模板)参考的该篇博客
我只用了建树和区间查找,但是下面是完整的线段树模板。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50010;
ll a[maxn],ans[maxn<<2],lazy[maxn<<2];
int n,q;
int index,len;
void pushup(int rt){//更新节点信息
ans[rt]=ans[rt<<1]+ans[rt<<1|1];
}
void build(int l,int r,int rt){//建树
if(l==r){
ans[rt]=a[l];
return ;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushup(rt);
}
void pushdown(int rt,int ln,int rn){//下推懒惰标记
//ln表示左子树元素结点个数,rn表示右子树结点个数
if(lazy[rt]){
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
ans[rt<<1]+=lazy[rt]*ln;
ans[rt<<1|1]+=lazy[rt]*rn;
lazy[rt]=0;
}
}
void add(int L,int C,int l,int r,int rt){//点更新
if(l==r){
ans[rt]+=C;
return ;
}
int mid=(l+r)>>1;
//pushdown(rt,mid-l+1,r-mid);//若既有点更新又有区间更新,就加上这句。
if(L<=mid){
add(L,C,l,mid,rt<<1);
}else{
add(L,C,mid+1,r,rt<<1|1);
}
pushup(rt);
}
void update(int L,int R,int C,int l,int r,int rt){//区间更新
if(L<=l&&r<=R){
ans[rt]+=C*(r-l+1);
lazy[rt]+=C;
return ;
}
int mid=(l+r)>>1;
pushdown(rt,mid-l+1,r-mid);
if(L<=mid) update(L,R,C,l,mid,rt<<1);
if(R>mid) update(L,R,C,mid+1,r,rt<<1|1);
pushup(rt);
}
ll query(int L,int R,int l,int r,int rt){//区间查询
if(L<=l && r<=R){
return ans[rt];
}
int mid=(l+r)>>1;
pushdown(rt,mid-l+1,r-mid);
ll ans=0;
if(L<=mid) ans+=query(L,R,l,mid,rt<<1);
if(R>mid) ans+=query(L,R,mid+1,r,rt<<1|1);
return ans;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,n,1);
cin>>q;
while(q--){
cin>>index>>len;
int l=index,r=index+len-1;
cout<<query(l,r,1,n,1)<<endl;
}
//system("pause");
return 0;
}
//建树 Build(1,n,1);
//点更新 Add(L,C,1,n,1);
//区间修改 Update(L,R,C,1,n,1);
//区间查询 int ANS=Query(L,R,1,n,1);
背包问题
背包九讲中的最简单问题:01背包问题。
二维dp可以简化成一维。
每个物品选择选或者不选,根据背包内的容量维护出最大值。
状态转移方程:
d
p
[
j
]
=
m
a
x
{
d
p
[
j
−
w
]
+
v
,
d
p
[
j
]
}
dp[j]= max\lbrace dp[j-w]+v,dp[j] \rbrace
dp[j]=max{dp[j−w]+v,dp[j]}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=10010;
ll dp[maxn];
struct node{
int wi;
int val;
}a[110];
int n,w;
int main(){
cin>>n>>w;
for(int i=1;i<=n;i++){
cin>>a[i].wi>>a[i].val;
}
for(int i=1;i<=n;i++){
for(int j=w;j>=a[i].wi;j--){
dp[j]=max(dp[j-a[i].wi]+a[i].val,dp[j]);
}
}
cout<<dp[w]<<endl;
//system("pause");
return 0;
}
最长回文子串(Manacher算法)
一下没缓过神来,看自己以前写的代码有点懵。
其实真的很简单。
Manacher首先有一个思想——所有的字符串都是奇数位的,怎么让它为奇数位呢,我们在每两个字符的中间插一个#,例如:abba–>a#b#b#a,这样构造出来的新字符串一定是奇数的。
讲清楚了前置问题,我们来引入正菜:
(
1
)
l
e
n
[
i
]
=
m
i
n
{
l
e
n
[
2
⋅
p
o
s
−
i
]
,
m
a
x
l
e
n
−
i
}
(1)len[i]=min\{len[2·pos-i],maxlen-i\}
(1)len[i]=min{len[2⋅pos−i],maxlen−i}
(
2
)
l
e
n
[
i
]
=
1
(2)len[i]=1
(2)len[i]=1
(
3
)
对
i
位
置
上
的
字
符
向
左
右
进
行
匹
配
,
更
新
l
e
n
[
i
]
的
值
,
更
新
p
o
s
,
m
a
x
l
e
n
(3)对i位置上的字符向左右进行匹配,更新len[i]的值,更新pos,maxlen
(3)对i位置上的字符向左右进行匹配,更新len[i]的值,更新pos,maxlen
p
o
s
pos
pos:当前最大的回文子串的中点。
m
a
x
l
e
n
maxlen
maxlen:当前最大的回文子串的右边界。
我们有了三个操作,接下来根据具体情况选择需要执行哪项操作:
(1)
i
<
m
a
x
l
e
n
i<maxlen
i<maxlen 具体的说就是在[0,i-1]的时候,已经有一个下标pos的边界更新到了maxlen,现在我们要找的i还在之前的边界里。这样i在pos的右边,pos的左边一定存在一个j与i关于pos对称(这也是我们为什么要构造奇数字符串),这样的话有三种子情况:
- 1 该串 l e n [ i ] = l e n [ j ] len[i]=len[j] len[i]=len[j]
- 2 超出 m a x l e n − i maxlen-i maxlen−i
- 3 恰好等于
如果是1、2 的情况,长度的选择即为 l e n [ i ] = m i n { l e n [ 2 ⋅ p o s − i ] , m a x l e n − i } len[i]=min\{len[2·pos-i],maxlen-i\} len[i]=min{len[2⋅pos−i],maxlen−i}
如果是3的情况,我们没法确认maxlen外面还有没有能回文的字符,所以先把该位置标记为 l e n [ i ] = 1 len[i]=1 len[i]=1,然后再执行第3个操作,匹配一遍,得到长度。
(2) i > = m a x l e n i>=maxlen i>=maxlen
只能先执行操作2,再执行操作3.
#include<bits/stdc++.h>
using namespace std;
string s;
string longest(string s){
string newstr="$#";
for(int i=0;i<s.size();i++){
newstr+=s[i];
newstr+="#";
}
vector<int> len(newstr.size(),0);
int pos=0,max=0;
int start=0,maxlen=0;
for(int i=1;i<newstr.size();i++){
if(i<max){
len[i]=min(len[2*pos-i],max-i);
}else{
len[i]=1;
}
while(i+len[i]<newstr.size() && i-len[i]>0 &&
newstr[len[i]+i]==newstr[i-len[i]]){
len[i]++;
}
if(i+len[i]>max){
pos=i;
max=i+len[i];
}
if(len[i]-1>maxlen){
start=(i-len[i])/2;
maxlen=len[i]-1;
}
}
return s.substr(start,maxlen);
}
int main(){
string p;
getline(cin,s);
p=longest(s);
int len=p.size();
cout<<len<<endl;
//system("pause");
return 0;
}
质数检验
质数n有性质:只能除以1和它本身,余数为0。暴力的判断方式就是在[2,n-1]一直做判断,如果有能被整除的数出现,说明它不是质数。简化一点的就是在[2,floor(sqrt(n))+1]一直做判断,我们分析这道题的数据范围为 2 < = S [ i ] < = 1 0 9 2 <= S[i] <= 10^9 2<=S[i]<=109,那么我们要先用质数筛(我用的线性筛,模板在蓝桥杯整理哪个博客中有)把需要的质数打表,( s q r t ( 1 0 9 ) < 1 0 5 sqrt(10^9)<10^5 sqrt(109)<105),我们的质数表打到 1 0 5 10^5 105即可,不会超时。为什么要用质数除n,举个例子,如果这个数不能被2整除,那肯定就不能被4整除,对于n去除2的倍数是毫无意义且浪费时间的事情,我们可以剪枝掉。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
int prime[maxn];
int vis[maxn];
ll x;
int Prim(int n){
int p=0;
vis[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
prime[++p]=i;
}
for(int j=1;j<=p&&i*prime[j]<=n;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
return p;
}
bool check(int len){
for(int i=1;i<=len && prime[i]<x;i++){
if(x%prime[i]==0) return false;
}
return true;
}
int main(){
int n;
cin>>n;
int len=Prim(maxn);
while(n--){
cin>>x;
if(check(len)) cout<<"Yes"<<"\n";
else cout<<"No"<<"\n";
}
//system("pause");
return 0;
}
机器人走方格
简单的二维递推式。题目明确规定只能向下或者向右走,所以(i,j)位置有多少种情况就是向上有多少种情况(i,j-1)+向左有多少种情况(i-1,j)的和。
数据范围:
2
<
=
m
,
n
<
=
1000
2 <= m,n <= 1000
2<=m,n<=1000
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1010;
const int mod=1e9+7;
int n,m;
ll dp[maxn][maxn];
int main(){
cin>>n>>m;
dp[1][1]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i==1 && j==1) continue;
dp[i][j]=(dp[i-1][j]+dp[i][j-1])%mod;
}
}
cout<<dp[n][m]<<endl;
system("pause");
return 0;
}
数据范围:
2
<
=
m
,
n
<
=
1000000
2 <= m,n <= 1000000
2<=m,n<=1000000
在更大的数据下,上述的方法显然会超时(TLE),那么我们去寻找规律,可以发现,这是一个取模的组合数问题。
将上述的dp矩阵顺时针旋转45°,就会发现这就是一个杨辉三角形,那么自然而然可以想到用组合数来解决。
从起点(左上角)到终点(右下角)的距离就是我们组合数C(n,m)中的n,
m
i
n
{
m
−
1
,
n
−
1
}
min\{m-1,n-1\}
min{m−1,n−1}就是组合数中的m,距离如何计算,可以把起点到终点的路线进行平移,向右需要走n-1步,向下需要走m-1步,总共的
n
=
m
+
n
−
2
n=m+n-2
n=m+n−2 .
因为题目中要求取模,所以不能简单的分子分母做商,采用费马小定理,写成
f
z
∗
q
p
o
w
(
f
m
,
m
o
d
−
2
)
fz*qpow(fm,mod-2)%mod
fz∗qpow(fm,mod−2)的形式,等同于不取模情况下的
f
z
/
f
m
fz/fm
fz/fm。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1010;
const int mod=1e9+7;
int n,m;
ll qpow(ll a,ll b){
ll res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
ll calc(ll n,ll m){
ll fz=1;
ll fm=1;
for(ll i=n;i>=m+1;i--){
fz=fz*i%mod;
}
for(ll i=1;i<=n-m;i++){
fm=fm*i%mod;
}
return fz*qpow(fm,mod-2)%mod;
}
int main(){
cin>>n>>m;
cout<<calc(m+n-2,min(m-1,n-1));
//system("pause");
return 0;
}
最长递增子序列
一种新思想:
- 1 a [ i ] > d p [ l e n ] a[i]>dp[len] a[i]>dp[len] a[i]是序列的第i个,dp[len]是选择的子序列的最后一个。在当前情况下,可以直接把a[i]加进子序列中。
- 2 a [ i ] < = d p [ l e n ] a[i]<=dp[len] a[i]<=dp[len] 二分找到第一个大于等于a[i]的下标,把值替换成a[i]。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50050;
ll a[maxn];
ll dp[maxn];
int n;
int pos,len;
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
dp[len]=a[1];
for(int i=1;i<=n;i++){
if(a[i]>dp[len]){
dp[++len]=a[i];
}else{
pos=lower_bound(dp,dp+len,a[i])-dp;
dp[pos]=a[i];
}
}
cout<<len+1<<endl;
return 0;
}
原根
a是p的原根,满足:p-1的所有质因子 p i p_i pi,都满足 a p − 1 / p i % p ! = 1 a^{p-1/p_i}\%p!=1 ap−1/pi%p!=1
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000005;
bool vis[N];
vector<int>p,b;
void init(){
memset(vis,false,sizeof(vis));
for(int i=2;i<=N;i++){
if(!vis[i]){
p.push_back(i);
for(int j=i+i;j<=N;j+=i)vis[j]=true;
}
}
}
void getnum(ll n){
int tmp=sqrt(n);
for(int i=0;i<p.size();i++){
if(n%p[i]==0){
b.push_back(p[i]);
while(n%p[i]==0)n/=p[i];
}
}
if(n>1)b.push_back(n);
}
ll qpow(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b&1) ans=ans*a%p;
b>>=1;
a=a*a%p;
}
return ans;
}
int main(){
ll x;
cin>>x;
init();
getnum(x-1);
for(int i=2;i<x;i++){
int flag=0;
for(int j=0;j<b.size();j++){
ll tmp=(x-1)/b[j];
if(qpow(i,tmp,x)==1){
flag=1;break;
}
}
if(!flag){
cout<<i<<endl;
break;
}
}
//system("pause");
return 0;
}
不知道算day几了,断更几天,昨天写河南省赛重现了一下,发现状态好可以单挑一个银,等能参加了再说。该蓝桥杯国赛了,练练dp吧,基础等国赛完了之后再慢慢更全。
又回来更新了,为了国赛和一些作业耽搁了几天,继续更,准备省赛了。
欧拉函数
欧拉函数求解:对于n,小于等于n的数中,与n互质的数的数目。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll phi(ll n){
ll res=n;
for(ll i=2;i<=n/i;i++){
if(n%i==0){
res=res/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1) res=res/n*(n-1);
return res;
}
int main(){
ll n;
cin>>n;
cout<<phi(n)<<endl;
//system("pause");
return 0;
}
矩阵乘法
写数组,写vector都行,用结构体比较好传参,简单明了。
矩阵乘法就是前行乘后列,用三维计算,得到结果输出即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn=101;
const int mod=1e9+7;
typedef long long ll;
struct Mat{
ll m[maxn][maxn];
};
ll n;
Mat mul(Mat x,Mat y){
Mat b;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
b.m[i][j]=0;
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
for(int k=0;k<n;k++){
b.m[i][j]=(b.m[i][j]%mod+(x.m[i][k]*y.m[k][j])%mod)%mod;
}
}
}
return b;
}
int main(){
Mat a,b;
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>a.m[i][j];
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>b.m[i][j];
}
}
Mat c=mul(a,b);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cout<<c.m[i][j]<<" ";
}
cout<<endl;
}
//system("pause");
return 0;
}
矩阵快速幂
特别注意:快速幂的 r e s res res矩阵,初始化成单位阵(主对角线为1,其余为0)
#include<bits/stdc++.h>
using namespace std;
const int maxn=101;
const int mod=1e9+7;
typedef long long ll;
struct Mat{
ll m[maxn][maxn];
};
ll n,m;
Mat mul(Mat x,Mat y){
Mat b;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
b.m[i][j]=0;
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
for(int k=0;k<n;k++){
b.m[i][j]=(b.m[i][j]%mod+(x.m[i][k]*y.m[k][j])%mod)%mod;
}
}
}
return b;
}
Mat qpow(Mat a,ll b){
Mat res;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
res.m[i][j]=0;
if(i==j) res.m[i][j]=1;
}
}
while(b){
if(b&1) res=mul(res,a);
a=mul(a,a);
b>>=1;
}
return res;
}
int main(){
Mat a,c;
cin>>n>>m;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>a.m[i][j];
}
}
c=qpow(a,m);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cout<<c.m[i][j]<<" ";
}
cout<<endl;
}
//system("pause");
return 0;
}
区间中最大的数
只用到建树和区间查询,建树的时候不断更新区间内最大值(并非累加值)为父节点即可。查询的时候依旧是查询最大值。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50010;
ll a[maxn],ans[maxn<<2],lazy[maxn<<2];
int n,q;
void pushup(int rt){//更新节点信息
ans[rt]=max(ans[rt<<1],ans[rt<<1|1]);
}
void build(int l,int r,int rt){//建树
if(l==r){
ans[rt]=a[l];
return ;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushup(rt);
}
void pushdown(int rt,int ln,int rn){//下推懒惰标记
//ln表示左子树元素结点个数,rn表示右子树结点个数
if(lazy[rt]){
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
ans[rt<<1]+=lazy[rt]*ln;
ans[rt<<1|1]+=lazy[rt]*rn;
lazy[rt]=0;
}
}
ll query(int L,int R,int l,int r,int rt){//区间查询
if(L<=l && r<=R){
return ans[rt];
}
int mid=(l+r)>>1;
pushdown(rt,mid-l+1,r-mid);
ll ans=0;
if(L<=mid) ans=max(ans,query(L,R,l,mid,rt<<1));
if(R>mid) ans=max(ans,query(L,R,mid+1,r,rt<<1|1));
return ans;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,n,1);
cin>>q;
ll l,r;
while(q--){
cin>>l>>r;
cout<<query(l+1,r+1,1,n,1)<<endl;
}
//system("pause");
return 0;
}
后话:求区间中第k大的数——主席树。
质数中的质数(质数筛法)
质数筛,用了埃氏筛,背起来简单。
用数组存一下质数,这样这个数组下标就是排第几的质数。
用一个数组存
[
1
,
m
a
x
n
]
[1,maxn]
[1,maxn]中这个数是不是质数,为了后面判断,这样能直接判断出下标是不是质数
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+100;
int prime[maxn];
int vis[maxn];
int Vis[maxn];
int Prim(int n){
int p=0;
memset(vis,0,sizeof(vis));
vis[0]=vis[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
prime[++p]=i;
Vis[i]=1;
for(int j=i+i;j<=n;j+=i){
vis[j]=1;
}
}
}
return p;
}
vector<int> ve;
int main(){
int n;
cin>>n;
int num=Prim(maxn-100);
for(int i=1;i<=num;i++){
if(Vis[i]&&prime[i]>=n){
cout<<prime[i]<<endl;
break;
}
}
//system("pause");
return 0;
}