又一道 GCD 问题
题目链接:nowcoder 213804
到牛客看:
题目大意
给你一个数组,要你求出从他们中选出 2 ∼ n 2\sim n 2∼n 个的 gcd 的最大值。
思路
看到这道题,我们考虑看有多少个数有 x x x 这个因子,设其为 f x f_x fx。
那我们分解一下质因子(用素数筛选法和 floor(sqrt()) 来加速),然后 dfs 一下,就可以得到一个数的每个因子,就从而求出了 f x f_x fx。
那有了这个,我们可以求出要选出 y y y 个数的 gcd 最大值是多少了。
(就是从后往前搜第一个 f i f_i fi 大于等于 y y y 的数)
那我们可以先弄出刚刚好是 y y y 个数的,然后再从后往前跑最大值。
然后就可以得出答案了。
具体实现可以看看代码。
代码
#include<cmath>
#include<cstdio>
#include<iostream>
using namespace std;
int n, a[100001], su[50001], all[100001], tmpn, tmp, ans[100001], maxn, zyz[1001], numm[1001];
bool susu[100100];
void getsusu() {
//素数筛选法筛出素数
susu[1] = susu[0] = 1;
for (int i = 2; i <= maxn + 100; i++)
if (!susu[i]) {
su[++su[0]] = i;
for (int j = i + i; j <= maxn + 100; j += i)
susu[j] = 1;
}
}
void dfs(int now, int sum) {
if (now == zyz[0] + 1) {
all[sum]++;
return ;
}
int tmpp = 1;
for (int i = 0; i <= numm[now]; i++) {
//对于这一个因数,选多少个乘
dfs(now + 1, sum * tmpp);
tmpp *= zyz[now];
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
maxn = max(maxn, a[i]);
ans[i] = 1;
}
getsusu();
for (int i = 1; i <= n; i++){
zyz[0] = 0;//分解质因数
tmpn = a[i];
tmp = floor(sqrt(a[i]));
for (int j = 1; su[j] <= tmpn && j <= tmp; j++) {
if (tmpn % su[j] == 0) {
zyz[++zyz[0]] = su[j];
numm[zyz[0]] = 0;
while (tmpn % su[j] == 0) {
numm[zyz[0]]++;
tmpn /= su[j];
}
}
}
if (tmpn != 1) {
zyz[++zyz[0]] = tmpn;
numm[zyz[0]] = 1;
}
dfs(1, 1);//标记每一个因数
}
for (int i = maxn; i >= 1; i--) {
if (ans[all[i]] == 1) ans[all[i]] = i;
}
for (int i = n - 1; i >= 2; i--)//选 i 个也可以在 i+x 个中选 i 个,不一定要刚刚好
ans[i] = max(ans[i + 1], ans[i]);
for (int i = 2; i <= n; i++) printf("%d ", ans[i]);
return 0;
}