题目链接: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;
}