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;
}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号