Max GCD(贪心、枚举)

题意

给定一个长度为N的整数序列:A1,A2,,AN

你可以执行如下操作0K次:

  • 选择两个整数ij满足ij,将Ai1Aj1(可能会出现负数)。

问最大的正整数x,满足在K次操作内,把所有Ai变成x的倍数(0为任意正整数的倍数)

题目链接:https://atcoder.jp/contests/abc136/tasks/abc136_e

数据范围

2N500
1Ai106
0K109

思路

看到这道题,我的第一反应是二分答案。但是仔细分析一下,发现不满足二分性质。一个较小的数可能不满足要求,但是另一个较大的数有可能是满足要求的,因此不能二分。

但是枚举可能的答案,然后check应该是一个正确的思路,因此需要找一个候选答案集合。

我们考虑到,经过若干操作之后,序列的和(s=i=1NAi)是不变的。并且,所有Ai都为x的倍数,那么s也应该为x的倍数。因此,所有s的因数为候选答案集合,集合元素个数为O(NmaxAi)

下面考虑如何check答案,这是一个经典贪心问题。假设当前枚举到的答案为x,将Aix取模,然后从小到大排序。我们希望接近于0的数都减到0,接近于x的数都加到x。因此,我们枚举分界点,分界点左边的元素全都减到0,分界点右边的元素全都加到x。判断一下减的次数与加的次数是否相等,以及是否K即可。这可以通过前缀和快速实现。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

typedef long long ll;

const int N = 510;

int n, k;
int a[N], b[N];
ll sum[N];

bool check(int x)
{
    for(int i = 1; i <= n; i ++) b[i] = a[i] % x;
    sort(b + 1, b + n + 1);
    for(int i = 1; i <= n; i ++) sum[i] = sum[i - 1] + b[i];
    for(int i = 1; i <= n; i ++) {
        ll t = (ll)x * (ll)(n - i);
        if(sum[i] == t - (sum[n] - sum[i]) && sum[i] <= k) {
            return true;
        }
    }
    return false;
}

int main()
{
    scanf("%d%d", &n, &k);
    ll tot = 0;
    for(int i = 1; i <= n; i ++) {
        scanf("%d", &a[i]);
        tot += a[i];
    }
    int ans = 1;
    for(int i = sqrt(tot); i >= 1; i --) {
        if(check(i)) ans = max(ans, i);
        if(check(tot / i)) ans = max((ll)ans, tot / i);
    }
    printf("%d\n", ans);
    return 0;
}
posted @   pbc的成长之路  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示