牛牛的最大兴趣组

牛牛的最大兴趣组 ⁡ \operatorname{牛牛的最大兴趣组}

题目链接: nowcoder 212979 ⁡ \operatorname{nowcoder\ 212979} nowcoder 212979

关于这场比赛

——>点我可以查看其它题目(目录)<——

到牛客看

——>点我跳转<——

题目

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

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

输入

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

输出

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

样例输入

4
4 2 16 27

样例输出

3

样例解释

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

数据范围

对于 10 % 10\% 10% 的测试数据,保证 1 ≤ n ≤ 10 , 1 ≤ a i ≤ 500 1 \leq n \leq 10,1 \leq a_i \leq 500 1n10,1ai500
对于 20 % 20\% 20% 的测试数据,保证 1 ≤ n ≤ 10 , 1 ≤ a i ≤ 1 0 9 1 \leq n \leq 10,1 \leq a_i \leq 10^9 1n10,1ai109
对于 30 % 30\% 30% 的测试数据,保证 1 ≤ n ≤ 150 , 1 ≤ a i ≤ 2 × 1 0 9 1 \leq n \leq 150,1 \leq a_i \leq 2 \times 10^9 1n150,1ai2×109
对于 40 % 40\% 40% 的测试数据,保证 1 ≤ n ≤ 1000 , 1 ≤ a i ≤ 2 × 1 0 9 1 \leq n \leq 1000,1 \leq a_i \leq 2 \times 10^9 1n1000,1ai2×109
对于 100 % 100\% 100% 的测试数据,保证 1 ≤ n ≤ 1 0 5 , 1 ≤ a i ≤ 2 × 1 0 9 1 \leq n \leq 10^5,1 \leq a_i \leq 2 \times 10^9 1n105,1ai2×109

思路

这道题是一道数学题。

要让两个数乘积是某个整数的三次方,就是要让他们的积的质因数分解出来的质数中,每个质数的个数一定是 3 3 3 的倍数。

那我们可以考虑这样先处理一下,把每一个有次方数作为因子的数都除掉所有的次方数,直到没有。

举个例子: 27 27 27 在处理完之后会变成 1 1 1 16 16 16 在处理完之后会变成 2 2 2 9 9 9 在处理完之后会变成 9 9 9

那我们会发现,要让处理完的某两个数乘起来是某个数的三次方,这两个数是一一对应的。
(注意:这里的一一对应是处理完的数,不是处理之前的数)
为什么呢?

  1. A数有 0 0 0 个质因子 x x x,那B数要跟ta对应,就必须要有 0 0 0 个质因子 x x x
  2. A数有 1 1 1 个质因子 x x x,那B数要跟ta对应,就必须要有 2 2 2 个质因子 x x x
  3. A数有 2 2 2 个质因子 x x x,那B数要跟ta对应,就必须要有 1 1 1 个质因子 x x x
  • A数或B数有更多的这个质因子?不可能,因为如果有,也会被前面的处理除掉

那我们就可以通过这个一一对应,知道A这一类的和B这一类的数不能同时存在。
那到底留下那一边呢?简单,就是留多的那一边,因为答案要求留下的数更多。

有一个要注意的就是:如果数处理完之后就变成了 1 1 1,就要单独处理,因为 1 1 1 是跟自己对应的。

考试时

想到了要处理数字,但是没有想到处理完的数字是一一对应的,就只能唯唯诺诺的打了个 n^2 30分代码。
在这里插入图片描述

代码

#include<map>
#include<cstdio>
#include<iostream>

#define ll long long

using namespace std;

ll n, x, nxt[100001], num[100001], from, to, tot, one, ans;
map <int, int> pd;
bool use[100001];

int main() {
    scanf("%lld", &n);
    for (ll i = 1; i <= n; i++) {
        scanf("%lld", &x);
        
        from = x;//最后求出原来的数除以所有立方数因子的积
        to = 1;//最后求出来是要让 from 可以不和要乘上什么
        //注意下面这里范围是 j^2 <= x 不是 j^3 <= x
        for (ll j = 2; j * j <= x; j++) {
            if (x % j) continue;
            while (x % (j * j * j) == 0) {
                x /= (j * j * j);
                from /= (j * j * j);
            }
            if (x % (j * j) == 0) {
                x /= (j * j);
                to *= j;//from 有两个,一共要三个,所以这里要给一个
            }
            else if (x % j == 0) {
                x /= j;
                to *= (j * j);//from 有一个,一共要三个,所以这里要给两个
            }
        }
        if (x > 1) to *= (x * x);//剩下一个素数,还要补两个
        
        if (from == 1) {//特殊处理只由立方数构成的数
            one = 1;
            continue;
        }
        
        if (!pd[from]) {//配对
            num[++tot] = 1;
            pd[from] = tot;
        }
        else num[pd[from]]++;
        if (!pd[to]) pd[to] = ++tot;
        nxt[pd[from]] = pd[to];//记录另一半的编号
    }
    
    for (ll i = 1; i <= tot; i++)
        if (!use[i]) {
            ans += max(num[i], num[nxt[i]]);
            //对于每一对,把少的一边全部不要
            use[i] = 1;
            use[nxt[i]] = 1;
        }
    
    printf("%lld", ans + one);//输出(one 就是上面的特殊处理)
    
    return 0;
}
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值