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;
}

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