CodeForces1325 E. Ehab's REAL Number Theory Problem(轉化爲最小環問題)

題意:

給一個長度爲n的數組a,數組中的元素滿足條件:每個數最多隻有7個因子
現在要你選出一段子序列,滿足子序列乘積是完全平方數,問子序列的最小長度是多少,輸出最小長度,無解則輸出-1
數據範圍:n<=1e5,a(i)<=1e6

思路:

完全平方數即質因子的冪次都是偶數的數。

一個數最多隻有7個因子,可以推出最多隻有兩種質因子,因爲三種質因子的數至少有8個因子。

如果某個數沒有奇數次冪的質因子,那麼它就是一個完全平方數,輸出1即可
如果某個數有一個奇數次冪的質因子,讓這個質因子和點1連無向邊
如果某個數有兩個爲奇數次冪的質因子,讓這兩個質因子間連無向邊

則圖的最小環就是答案。

最小環的計算常用的是floyd,但是複雜度太高,這題顯然不行。

考慮到邊權爲1,可以枚舉所有點作爲起點+bfs計算最小環。
但是枚舉所有點作爲起點也會超時。
考慮最大範圍數據,因爲數據最大範圍爲1e6,則大於根號1e6(即大於1e3的點),是不可能和同樣大於1e3的點相連的(因爲不可能存在一個數有兩個大於1e3的質因子),只可能和小於1e3的點相連,因此只需要枚舉小於等於1e3的點作爲起點就就行了。

ps:
bfs求最小環是第一次見到,不是很懂原理。
畫了一下bfs的分層圖,發現當x搜到一個已經搜過的v的時候,最小環應該是d(x)+d(v)+1-d(LCA(x,v)),但是大家的代碼中都是直接對d(x)+d(v)+1取min,沒有管後面的LCA,稍微想了一下發現是因爲枚舉所有數作爲根節點,總會枚舉到他們的LCA作爲根,這時候就可以上面的式子就可以去掉後面的LCA了,也就是直接取min總會取到正確答案。

對於這題來說,不枚舉大於1e3的作爲LCA是因爲枚舉到與他相連的點作爲LCA也能計算出答案。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e6+5;
int head[maxm],nt[maxm<<1],to[maxm<<1],w[maxm<<1],cnt,idx;
int d[maxm],mark[maxm];
int ans=1e9;
int n;
void add(int x,int y,int z){
    cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y,w[cnt]=z;
}
bool isprime(int x){
    for(int i=2;i*i<=x;i++){
        if(x%i==0)return 0;
    }
    return 1;
}
void bfs(int st){
    queue<int>q;
    q.push(st);
    memset(d,-1,sizeof d);
    for(int i=1;i<=idx;i++)mark[i]=0;//清空邊標記
    d[st]=0;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=nt[i]){
            if(mark[w[i]])continue;//回邊跳過
            mark[w[i]]=1;
            int v=to[i];
            if(d[v]==-1){
                d[v]=d[x]+1;
                q.push(v);
            }else{
                ans=min(ans,d[v]+d[x]+1);
            }
        }
    }
}
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        int x;
        scanf("%lld",&x);
        int a=0,b=1;
        for(int j=2;j*j<=x;j++){
            if(x%j==0){
                int cnt=0;
                while(x%j==0){
                    cnt++;
                    x/=j;
                }
                if(cnt%2){
                    if(!a)a=j;
                    else b=j;
                }
            }
        }
        if(x!=1){
            if(!a)a=x;
            else b=x;
        }
        if(!a){//如果本身就是完全平方數,則答案爲1
            puts("1");
            return 0;
        }
        add(a,b,idx);
        add(b,a,idx);
        idx++;
    }
    for(int i=1;i<=1000;i++){//max(a[i])=1e6 -> sqrt(max(a[i]))=1e3
        if(isprime(i)){
            bfs(i);
        }
    }
    if(ans==1e9)ans=-1;
    printf("%lld\n",ans);
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章