221016周末作业 分组
题目描述
Tom 的班级中有 \(n\) 个人,他们的性格各不相同。
Tom 现在想要从这 \(n\) 个人中选出一些人组成一个兴趣小组,但是他想让参加这个兴趣小组的人数尽可能的多。但是他又不想让其中有任何一对人之间由于性格问题产生矛盾。
具体来说,如果这个兴趣小组中出现两个人性格值的乘积开三次方根是一个正整数,就认为他们两个性格不合。
比如一个性格值为 \(2\) 的同学和一个性格值为 \(4\) 的同学就是性格不合的,因为 \(2\times 4=8\),而一个性格值为 \(2\) 的同学和一个性格值为 \(8\) 的同学性格相合,可以出现在同一个兴趣小组中,因为 \(2\times 8=16\),\(16\) 开三次方根不是一个正整数。
请你告诉 Tom,他们班的同学组成的最大兴趣小组的人数是多少。
输入格式
第一行输入一个正整数 \(n\) 表示牛牛所在的班级中的人数。
接下来输入一行 \(n\) 个正整数 \(a_i\)表示每个人的性格值。
输出格式
输出一行一个正整数,表示最大兴趣小组的人数。
解析
首先一个数可以写成\(a = x_1 * y_1 ^ 3\)的形式,\(b=x_2 * y _ 2 ^ 3\),a和b如果性格不合(即\(a * b = z ^ 3\)),那么\(x * y\)肯定也是某个数的三次方,所以我们不妨将所有的数中的某个质因数的三次方消去化简,化简后两个数不能配对的这种情况就有唯一性,比如2和4不能配对,4和16不能配对,16除以\(2 ^ 3\)结果为2,还是2和4不能配对。
化简时有一种特殊情况就是该数本身就是三次方的形式,化简后结果为1,像这样的数我们最多只能选一个。对于其他情况我们再找不能和该数配对的数,取两者中map的最大值(map中存的是化简后的数的个数,因为化简后可能有重复的数)。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, a[N], sum, ans, to[N];
map<int, int> h;
map<int, bool> vis;//数组存不下,就用map
signed main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
int x = a[i], y = 1;//y计算不能和x配对的数
for (int j = 2; j * j <= x; j ++) {
if (x % j) continue;
while (x % (j * j * j) == 0) {//消去三次方
x /= (j * j * j);
a[i] /= (j * j * j);
}
if (x % (j * j) == 0) {//x能被j方除,那么y能被j除
x /= (j * j);
y *= j;
}
else if (x % j == 0) {//同上
x /= j;
y *= (j * j);
}
}
if (x > 1) y *= x * x;
if (a[i] == 1) {sum = 1; continue;}
h[a[i]] ++; to[i] = y;
}
for (int i = 1; i <= n; i ++)
if (!vis[a[i]] && a[i] != 1) {
ans += max(h[a[i]], h[to[i]]);
vis[a[i]] = 1, vis[to[i]] = 1;
//二者中只能取一个,所以取一个后两者都要打上标记
}
cout << ans + sum;
return 0;
}