zoukankan      html  css  js  c++  java
  • *CodeForces 1325E

    题意:

      给定一个数组 (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
    ");
            return 0;
        }
        int len=unique(a+1,a+1+n)-a-1;
        if(len<n)//有相同元素
        {
            printf("2
    ");
            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
    ",ans==N?-1:ans);
        return 0;
    }
    
    

    参考博客

  • 相关阅读:
    git常用命令学习(转)
    论docker中 CMD 与 ENTRYPOINT 的区别(转)
    常见算法:C语言求最小公倍数和最大公约数三种算法
    iPhone开发【一】从HelloWorld開始
    网页代码优化
    北京簋街 美食全然攻略 + 簋街好吃的夜宵去处-----店铺介绍大全
    strtok和strtok_r
    Swift 编程语言新手教程
    java中获取系统属性以及环境变量
    读《自由人》
  • 原文地址:https://www.cnblogs.com/1024-xzx/p/12507534.html
Copyright © 2011-2022 走看看