2020牛客NOIP赛前集训营-提高组(第一场)C 牛牛的最大兴趣组

链接:https://ac.nowcoder.com/acm/contest/7604/C
来源:牛客网

题目描述

牛牛的班级中有n个人,他们的性格各不相同。
牛牛现在想要从这n个人中选出一些人组成一个兴趣小组,但是他想让参加这个兴趣小组的人数尽可能的多。但是他有不想让其中有任何一对人之间由于性格问题产生矛盾。
具体来说,如果这个兴趣小组中出现两个人性格值的乘积开三次方根是一个正整数,就认为他们两个性格不合。
比如一个性格值为2的同学和一个性格值为4的同学就是性格不合的,因为2*4=8,而一个性格值为2的同学和一个性格值为8的同学性格相合,可以出现在同一个兴趣小组中,因为2*8=16,16开三次方根不是一个正整数。

请你告诉牛牛,他们班的同学组成的最大兴趣小组的人数是多少。

输入描述:

第一行输入一个正整数n表示牛牛所在的班级中的人数。
接下来输入一行n个正整数aia_iai表示每个人的性格值。

输出描述:

输出一行一个正整数,表示最大兴趣小组的人数。
示例1

输入

4
4 2 16 27

输出

3

说明

1号和2号同学性格值的乘积为8=238=2^38=23,性格不合,1号和3号同学性格值的乘积为64=4364=4^364=43,性格不合。
选取第2,3,4号同学组成一个最大兴趣组,共3人。

备注:

对于10%10\%10%的测试数据,保证1≤n≤10,1≤ai≤5001 leq n leq 10,1 leq a_i leq 5001n10,1ai500
对于20%20\%20%的测试数据,保证1≤n≤10,1≤ai≤1091 leq n leq 10,1 leq a_i leq 10^91n10,1ai109
对于30%30\%30%的测试数据,保证1≤n≤150,1≤ai≤2×1091 leq n leq 150,1 leq a_i leq 2 imes 10^91n150,1ai2×109
对于40%40\%40%的测试数据,保证1≤n≤1000,1≤ai≤2×1091 leq n leq 1000,1 leq a_i leq 2 imes 10^91n1000,1ai2×109
对于100%100\%100%的测试数据,保证1≤n≤105,1≤ai≤2×1091 leq n leq 10^5,1 leq a_i leq 2 imes 10^91n105,1ai2×109

考虑到有1e5个数,暴力会超时,所以要想其他办法,任意两个相乘不能是立方数,所以2和16可以被认为等价的(16=(2*2*2)*2)
,ai最大到2e9,我们先把2到sqrt(2e9)的数筛一遍,然后分别除以他们的立方数因子,于是,每个数的重复的因子个数都小于3,
利用map计数再放到vector把数量从大到小排序,每次把当前的加到答案上,若对应的矛盾数存在则把矛盾数清零,这里有个细节要注意一下
我们筛的是2到sqrt(2e9),当我们再找矛盾数的时候如果用试除法,可能会忽略大素数,如(1e9+7)*2,只有最后留下的x是1才是合数,否则m=m*x*x(详细见代码)
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
#define forn(i,n) for(int i=0;i<n;i++)
#define form(i,n) for(int i=1;i<=n;i++)
const int N=1e5+90;
#define eps 1e-10
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> P ;
ll n,a[N],pr[N],p[N];
vector<ll>b;
vector<P>v;
unordered_map<ll,ll>mp;
int num;
void primes(){
    ll n=sqrt(2e9+0.5);
    for(int i=2;i<=n;i++){
        if(!p[i]) {
            p[i] = i;
            pr[++num] = i;
        }
        for (int j = 1; j <= num; j++) {
                if (pr[j] > n / i || pr[j] > p[i])break;
                p[i * pr[j]] = pr[j];
        }
    }
}
ll divide(ll x){//找对应的乘数
    ll m=1;
    for(int i=1;i<=num&&pr[i]<=x;i++){
        int tmp=3;
        if(x%pr[i]==0){
            x/=pr[i];
            tmp--;
            if(x%pr[i]==0){
                x/=pr[i];
                tmp--;
            }
            while (tmp){
                if(m>2e9)return -1;
                m=m*pr[i];
                tmp--;
            }
        }
    }
    if(x!=1)m=x*x;
    return m;
}
bool cmp(P x,P y){
    return x.first>y.first;
}
int main(){
    primes();//筛一下素数
    for(ll i=1;pr[i]*pr[i]<=2e9/pr[i]&&i<=num;i++)b.push_back(pr[i]*pr[i]*pr[i]);//把不超过ai的每个素数的立方存进去
    cin>>n;
    form(i,n) {
        scanf("%lld", &a[i]);
        for(auto it=b.begin();(*it)<=a[i]&&it!=b.end();it++){
            while (a[i]%(*it)==0)//消灭每个立方数的因子
                a[i]=a[i]/(*it);
        }
        mp[a[i]]++;
    }
    for(auto it:mp)v.push_back({it.second,it.first});//存到vector里方便数量按less排序
    sort(v.begin(),v.end(),cmp);
    ll ans=0;
    for(auto it:v){
        if(mp[it.second]==0)continue;
        if(it.second==1){//有1的只加1
            ans++;
            continue;
        }
        ans=ans+it.first;
        mp[divide(it.second)]=0;//因为按数量less排序的,所以若存在对应的乘数直接把数量归零
    }
    printf("%lld
",ans);
}
 
原文地址:https://www.cnblogs.com/ilikeeatfish/p/13842227.html