daimayuan每日一题- 最大公约数

题目link
学习博客


思路

答案 a n s = g = g c d ( 第 一 段 , 第 二 段 . . . . . 第 k 段 ) ans = g = gcd(第一段,第二段.....第k段) ans=g=gcd(,.....k),因为每一段都是 g g g的倍数,所以 ∑ a i \sum a_i ai 也是 g g g的倍数,因而答案为 ∑ a i \sum a_i ai的因子

这样可以枚举 ∑ a i \sum a_i ai 的因子 x x x,来计算该因子可以将环分成几段,用 c o t cot cot来记录因子 x x x将环分成的个数的最大值,记录答案 r e s [ c o t ] = m a x ( r e s [ c o t ] , x ) res[cot] = max(res[cot],x) res[cot]=max(res[cot],x), 表示为将环分成 c o t cot cot段的最大价值为 m a x ( r e s [ c o t ] , x ) max(res[cot],x) max(res[cot],x),不同因子下cot会重复计算,取最大的因子作为答案

计算因子 x x x能将环分成几段
我们先讨论不是环的情况
即一个数组如何计算出因数x能将数组分成几段连续和为 x x x倍数的区间,这个问题就是daimayuan div1每日一题的 选数 理解这个题后就很轻松解决这个问题,有详细解释

最后利用后缀数组更新一下最大值

for(int i = n;i>=1;i--) ans[i] = max(ans[i+1],ans[i]);

这是因为若你分成 n n n段的最大价值大于分成 n − 1 n-1 n1段的最大价值,那么为什么不用 n n n段中的2段看做 1 1 1段,这样就变成 n − 1 n-1 n1段了,故后利用后缀数组更新一下答案

当为环时 规定 s u m [ i ] sum[i] sum[i] a [ i ] a[i] a[i] 前缀和数组 当 s u m [ i ] % x sum[i]\%x sum[i]%x = = == == s u m [ j ] % x sum[j]\%x sum[j]%x 时 根据上面讨论可知 区间 [ i + 1 , j ] [i+1,j] [i+1,j]和为 x x x的倍数,那么环剩下部分区间和也一定是 x x x的倍数,记 s u m [ i ] % x sum[i]\%x sum[i]%x c n t cnt cnt个,那么就将环分成了 c n t cnt cnt
计算方式如下

void cal(int x){
    int cot = 0;
    map<int,int>mp;
    forr(i,1,n){
        mp[sum[i]%x]++;
        cot = max(mp[sum[i]%x],cot);
    }
    ans[cot] = max(ans[cot],x);
}

后更新一下后缀数组输出即可

code

int n;
int ans[2050];
int a[2050];
void cal(int x){
    int cot = 0;
    map<int,int>mp;
    forr(i,1,n){
        mp[a[i]%x]++;
        cot = max(mp[a[i]%x],cot);
    }
    ans[cot] = max(ans[cot],x);
}

signed main()
{
    cin>>n;

    forr(i,1,n)cin>>a[i],a[i] += a[i-1];
    int sum = a[n];
    for(int i = 1;i <= sum/i;i++){
        if(sum%i==0){
            cal(sum/i);
            cal(i);
        }
    }   

    for(int i = n;i >= 1;i--) ans[i] = max(ans[i],ans[i+1]);
    forr(i,1,n) cout << ans[i] <<" \n"[i==n];

    return 0;
}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值