题目链接:https://ac.nowcoder.com/acm/problem/212979

到主站看:https://blog.csdn.net/weixin_43346722/article/details/109140884

题目

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

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

输入

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

输出

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

样例输入

4
4 2 16 27

样例输出

3

样例解释

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

数据范围

对于 的测试数据,保证
对于 的测试数据,保证
对于 的测试数据,保证
对于 的测试数据,保证
对于 的测试数据,保证

思路

这道题是一道数学题。

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

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

举个例子: 在处理完之后会变成 在处理完之后会变成 在处理完之后会变成

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

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

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

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

考试时

想到了要处理数字,但是没有想到处理完的数字是一一对应的,就只能唯唯诺诺的打了个 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;
}