【nowcoder 213804】又一道 GCD 问题

又一道 GCD 问题

题目链接:nowcoder 213804

到牛客看:

——>点我跳转<——

题目大意

给你一个数组,要你求出从他们中选出 2 ∼ n 2\sim n 2n 个的 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;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值