daimayuan 131. 最大公约数(思维)

题意:

环上有n个正整数。现将环切成k段,每段至少一个数字。

一个切分方案的价值为每段数字和的最大公约数。对k=1,2,…,n输出最大价值。

\(n\le 2000,1\le a_i\le 5e7\)

思路:

价值一定是 \(\sum a_i\) 的因数!

另外如果分成 \(k+1\) 段的 gcd 是 \(g\),那也可以将其中的两段合并,变成 \(k\) 段,所以 \(k\) 段的 gcd 也至少是 \(g\)。也就是说最后要取后缀 max

枚举总和的因数 \(d\),对于每个 \(d\),前缀和数组中模 \(d\) 同余的位置都能切一刀。根据模 \(d\) 余 0 可以切成 \(c_0\) 段,当然也可合并其中的相邻段得到更少的段;根据模 \(d\) 余 0 可以切成 \(c_0\) 段,……

听起来好麻烦,要用组合数和容斥慢慢推吗?非也非也。因为最后要取后缀 max,所以只需拿 \(d\) 来更新最多段数的答案。

ll n, a[N], ans[N];

void cal(ll d) {
    unordered_map<int, int> mp;
    int c = 0;
    for(int i = 1; i <= n; i++)
        c = max(c, ++mp[a[i]%d]);
    ans[c] = max(ans[c], d);
}

signed main() {
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i], a[i] += a[i-1];

    for(int i = 1; i <= a[n]/i; i++)
        if(a[n] % i == 0) cal(i), cal(a[n]/i);

    for(int i = n; i; i--) ans[i] = max(ans[i], ans[i+1]);
    for(int i = 1; i <= n; i++) cout << ans[i] << endl;
}

posted @ 2022-03-20 21:12  Bellala  阅读(93)  评论(0)    收藏  举报