Codeforces Round #473 (Div. 2) D. Mahmoud and Ehab and another array construction task【素因数分解】【构造】

题目链接:https://codeforc.es/problemset/problem/959/D

题目大意:给定一个序列aa,要你找到一个大于等于原序列的字典序最小的新序列bb,使其满足:bi>=2b_i>=2,且1<=i,j<=n,gcd(bi,bj)=11<=i,j<=n,gcd(b_i,b_j)=1

思路:我们可以对原序列的每个数进行素因数分解,并对素因数的倍数进行标记,如果当前数已被标记,那就从当前数开始找到下一个未被标记的数,并且对找到的合法数也进行素因数分解,标记素因数倍数,而且此时更新flagflag,以后的数就可以从小数开始了。

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章