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