*CodeForces 1325E - Ehab's REAL Number Theory Problem【质因子+最小环】

题意:

  给定一个数组 \(a\) ,数组中任意一个元素的因子数不超过 \(7\) ,找出一个最短的子序列,满足该子序列之积为完全平方数。输出其长度。
数据范围:\(1≤n≤10^5,1≤a_i≤10^6\)

分析:

  首先,对于数组中的每个元素,如果其因子中包含有一个完全平方数,那么可以把该完全平方数除去,不影响最后的结果。
  然后,可以发现,当一个数的因子个数 \(\leq 7\) 时,其包含的质因子个数 \(\leq 2\)。(如果有 \(3\)个质因子,那么至少有 \(8\) 个因子)当我们把每个数因子中的完全平方数除去后,每个数会变成 \(1,p,pq\)\(p,q\) 均为质数)中的一种。接下来,建一个图,图中顶点为质数和 \(1\),边为数组中元素化简后得到的数字。那么图的意义是什么呢?假设我们从点 \(p\) 经过一条边走到点 \(q\),那么我们就可以得到 \(p*q\)。要想最后得到一个完全平方数,那么必然会有一个点走两次,即整条路径是一个环。要使路径最短,就是要求一个最小环的长度。
  我们可以直接对每个点用一遍 \(bfs\) 来找到最小环,复杂度为:\(O(N*M)\),显然不行。但其实可以发现,对于两个都大于 \(\sqrt{a_{max}}\)的点,它们之间是不可能连边的,各自只能和更小的数的点连边,所以对于这些点,不用以它们为源点进行 \(bfs\)

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int a[N],dis[N],ans,num;
bool vis[N];
vector<int>pic[N],prime;
queue<int>que;
void read(int &x)
{
    x=0;
    int f=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        x=(x<<3)+(x<<1)+ch-'0';
        ch=getchar();
    }
    x*=f;
}
int init(int x)
{
    for(int i=2;i*i<=x;i++)
    {
        if(x%i)
            continue;
        int cnt=0;
        while(x%i==0)
        {
            x/=i;
            cnt++;
        }
        if(cnt&1)
            x*=i;
    }
    return x;
}
void bfs(int s)
{
    while(!que.empty())
        que.pop();
    for(int i=0;i<num;i++)
    {
        dis[prime[i]]=N;
        vis[prime[i]]=0;
    }
    vis[s]=1;
    que.push(s);
    dis[s]=0;
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        vis[now]=0;
        for(int i=0;i<pic[now].size();i++)
        {
            int t=pic[now][i];
            if(dis[t]>dis[now]+1)
            {
                dis[t]=dis[now]+1;
                que.push(t);
                vis[t]=1;
            }
            else if(vis[t])
                ans=min(ans,dis[t]+dis[now]+1);
        }
    }
}
int main()
{
    int n,maxn=-1;
    read(n);
    for(int i=1;i<=n;i++)//预处理,消除完全平方的因子
    {
        read(a[i]);
        maxn=max(a[i],maxn);
        a[i]=init(a[i]);
    }
    sort(a+1,a+1+n);
    if(a[1]==1)
    {
        printf("1\n");
        return 0;
    }
    int len=unique(a+1,a+1+n)-a-1;
    if(len<n)//有相同元素
    {
        printf("2\n");
        return 0;
    }
    for(int i=1;i<=len;i++)//只有p,pq;1的情况已经被排除
    {
        vector<int>tmp(2,-1);
        tmp[0]=a[i];
        for(int j=2;j*j<=a[i];j++)
        {
            if(a[i]%j==0)
            {
                a[i]/=j;
                tmp[0]=j;
                if(a[i]>1)
                    tmp[1]=a[i];
                break;
            }
        }
        if(tmp[1]==-1)
            tmp[1]=1;
        for(int j=0;j<tmp.size();j++)
            prime.push_back(tmp[j]);
        pic[tmp[0]].push_back(tmp[1]);
        pic[tmp[1]].push_back(tmp[0]);
    }
    sort(prime.begin(),prime.end());
    num=unique(prime.begin(),prime.end())-prime.begin();
    ans=N;
    for(int i=0;i<num;i++)
    {
        if(1LL*prime[i]*prime[i]>=maxn)//注意数据范围:如果溢出,每个数都会找一遍,会t
            break;
        bfs(prime[i]);
    }
    printf("%d\n",ans==N?-1:ans);
    return 0;
}

参考博客

posted @ 2020-03-16 22:32  xzx9  阅读(271)  评论(0编辑  收藏  举报