[题解] 完美数
题目大意
给定 \(n\) 个数字,将这 \(n\) 个数字乘起来。找到 \(1\) 个数字 \(k\) ,使得 \(k!\) 能被 \(n\) 整除。求最小的 \(k\) 。
思路
先将 \(n\) 个数字分别质因数分解,用桶 \(B1\) 把这些质因数的个数统计起来。
按照题目的意思,需要找到一个 \(k!\) ,将其质因数分解,也把分解的这些质因数装进另一个桶 \(B2\) 里,使得桶 \(B2\) 里所有元素的个数都大于桶 \(B1\) 里面所有元素的个数。
怎么来找这个桶呢?可以二分枚举,找出答案。
这里分享另一种做法。
对于桶里面的每一个元素,都可以找到 \(1\) 个最小满足含有这个元素的乘积的倍数。描述有一些模糊,举个例子:
有 \(2\) 个数字,\(81\) 和 \(45\)。将这两个数字分解,则桶 \(B1\) 里,\(3\) 的个数为 \(6\) ,\(5\) 的个数为 \(1\) 。则最小满足 \(6\) 个 \(3\) 的数字为 \(15\)。其中, \(3\) 做 \(1\) 个贡献, \(6\) 做 \(1\) 个贡献, \(9\) 做 \(2\) 个贡献, \(12\) 做 \(1\) 个贡献, \(15\) 做 \(1\) 个贡献。简单来说,这个数字含有多少个 \(3\) ,就做多少贡献。可以直接暴力枚举,枚举到满足条件即可,这个数据枚举到 \(15\) 。对于 \(5\) 同理,枚举到 \(5\) 即可。最后取这两个数的最大值,因为 \(15!\) 就包含了 \(5!\) ,而且两质数之间互不干扰,没有交集。故而答案为 \(15\)。
总结
先用桶存储这 \(n\) 个数字的质因数个数,找桶中每个元素,满足这个元素的最小的 \(k\) ,取最大值来满足条件即可。
C++代码
#include <cstdio>
#define Max(a, b) ((a) > (b) ? (a) : (b))
void Quick_Read(int &N) {
N = 0;
int op = 1;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-')
op = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
N = (N << 1) + (N << 3) + (c ^ 48);
c = getchar();
}
N *= op;
}
const int MAXN = 1e5 + 5;
int bucket[MAXN];
bool flag[MAXN], vis[MAXN];
int p[MAXN];
int n, cnt, num;
void Primes() {
int k = 100000;
flag[1] = 1;
for(int i = 2; i <= k; i++) {
if(!flag[i])
p[++cnt] = i;
for(int j = 1; p[j] * i <= k && j <= cnt; j++) {
flag[p[j] * i] = true;
if(i % p[j] == 0)
break;
}
}
}
int main() {
int A;
Primes();
Quick_Read(n);
for(int i = 1; i <= n; i++) {
Quick_Read(A);
for(int j = 1; j <= cnt && A != 1; j++) {
if(A <= 100000 && !flag[A])
break;
while(A % p[j] == 0) {
A /= p[j];
bucket[p[j]]++;
if(!vis[p[j]])
num++;
vis[p[j]] = true;
}
}
if(!vis[A] && A != 1)
num++;
vis[A] = true;
bucket[A]++;
}
int ans = 0;
for(int i = 2; i <= 100000; i++) {
if(!bucket[i])
continue;
int j = 0, product, maxn = 0;
while(bucket[i] > 0) {
j++;
product = j * i;
while(product % i == 0) {
product /= i;
bucket[i]--;
}
maxn++;
}
ans = Max(ans, maxn * i);
}
printf("%d", ans);
return 0;
}