UVALive 7638

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/peter_xiazhen/article/details/77816059

题目大意:两个结点之间的gcd>1,那么这两个结点就可以建边,问最终有几个联通块。

题目思路:想到了并查集,但是单纯使用就T掉了O(n^2)。看了网上别的大神的博客,大体思路是这样的:把每个数进行质因数分解,然后把这个数和他的质因数建边。那么只要有相同质因数的两个数都会在同一个联通块里,因为x1<=1e6,那么遍历1~1e6的所有数字,该有边的肯定会在一个联通块里面O(n)。(比赛时候没想到,下回注意,放在这里提醒自己)。

代码如下:


#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxm=1000005;
int n,a[maxm];
int par[maxm],rak[maxm];
bool vis[maxm];
int ans;
vector<int> g[maxm];
void inite()
{
    for(int i=1;i<=maxm;i++)
    {
        par[i]=i;
        rak[i]=0;
    }
}
int find(int x)
{
    if(par[x]==x) return x;
    else {
            return par[x]=find(par[x]); 
    }
}
void unite(int x,int y)
{
    x=find(x);
    y=find(y);
    if(x==y) return;
    if(rak[x]<rak[y]){
    par[x]=y;
    }
    else{
        par[y]=x;
        if(rak[x]==rak[y]) rak[x]++;
    }
}
void solve()
{
    for(int i=2;i<=maxm;i++)
    for(int j=i;j<=maxm;j+=i)
    g[j].push_back(i);
}//把数和质因数建边啊。
int main()
{
    solve();//o(n)复杂度。
    int t,kase=1;
    scanf("%d",&t);
    while(t--)
    {   
        inite(); 
        ans=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            for(int j=0;j<g[a[i]].size();j++) unite(a[i],g[a[i]][j]);
        }
        memset(vis,false,sizeof(vis));
        for(int i=0;i<n;i++)
        {
            int f=find(a[i]);
            if(!vis[f])
            {
                ans++;
                if(a[i]!=1) vis[f]=true;
            } 
        }
        printf("Case %d: %d\n",kase++,ans);
    }
    return 0;
}



猜你喜欢

转载自blog.csdn.net/peter_xiazhen/article/details/77816059
今日推荐