題目鏈接:https://codeforc.es/problemset/problem/959/D
題目大意:給定一個序列,要你找到一個大於等於原序列的字典序最小的新序列,使其滿足:,且
思路:我們可以對原序列的每個數進行素因數分解,並對素因數的倍數進行標記,如果當前數已被標記,那就從當前數開始找到下一個未被標記的數,並且對找到的合法數也進行素因數分解,標記素因數倍數,而且此時更新,以後的數就可以從小數開始了。
AC代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e6+10;
int vis[maxn];
int ans[maxn];
int visprime[maxn];
int l=2;//標記上一次所使用到的最後一個合法的數,下次直接從l+1開始繼續找,不能又從2開始,因爲這樣會超時
int main()
{
int n;
int t;
scanf("%d",&n);
int flag=0;
for(int i=0;i<n;i++){
scanf("%d",&t);
if(flag==1){//說明之前已經有一個位置的數大於原始序列了,此時只需要儘可能地選一個最小的合法的數
while(l<maxn){
if(vis[l]==0){
break;//找到合法的未被使用過的第一個數
}
l++;
}
ans[i]=l;
int ll=l;
for(int j=2;j*j<=ll;j++){//分解ll這個數中的素因子,並對素因子的倍數打標記
if(ll%j==0){
while(ll%j==0){
ll/=j;
}
if(visprime[j]==0){//用一個visprime[]數組標記素因數是否已經被標記過了,如果未被標記就標記,不然就不用標記,這樣可以節省時間
for(int k=j;k<maxn;k+=j){
vis[k]=1;
}
visprime[j]=1;
}
}
}
if(ll>1){
if(visprime[ll]==0){
for(int k=ll;k<maxn;k+=ll){
vis[k]=1;
}
visprime[ll]=1;
}
}
vis[ans[i]]=1;
}
else{//當前位置之前的數都是原序列,並且合法
if(vis[t]==0){//當前這個數未被使用過並且它的素因子也未被使用過,直接可以用
ans[i]=t;
for(int j=2;j*j<=t;j++){
if(t%j==0){
while(t%j==0){
t/=j;
}
if(visprime[j]==0){
for(int k=j;k<maxn;k+=j){
vis[k]=1;
}
visprime[j]=1;
}
}
}
if(t>1){
if(visprime[t]==0){
for(int k=t;k<maxn;k+=t){
vis[k]=1;
}
visprime[t]=1;
}
}
vis[ans[i]]=1;
}
else{//當前數不合法
while(1){
if(vis[t]==0){
break;//遍歷找到下一個合法的數
}
t++;
}
ans[i]=t;
for(int j=2;j*j<=t;j++){
if(t%j==0){
while(t%j==0){
t/=j;
}
if(visprime[j]==0){
for(int k=j;k<maxn;k+=j){
vis[k]=1;
}
visprime[j]=1;
}
}
}
if(t>1){
if(visprime[t]==0){
for(int k=t;k<maxn;k+=t){
vis[k]=1;
}
visprime[t]=1;
}
}
flag=1;//更新flag,這時這個位置的數已經大於原序列了,以後的數可以從小數開始選了,
vis[ans[i]]=1;
}
}
}
for(int i=0;i<n-1;i++){
printf("%d ",ans[i]);
}
printf("%d\n",ans[n-1]);
return 0;
}