代码源每日一题div1 最大公约数

Daimayuan Online Judge

思路:数学枚举+环上问题
如果将环切成 k k k段,那么有 x = g c d ( d 1 , d 2 , ⋅ ⋅ ⋅ , d k ) x=gcd(d_1,d_2,···,d_k) x=gcd(d1,d2,,dk)
我们可以发现 d i = x ∗ k i d_i=x*k_i di=xki,因此 s u m = ∑ d i = x ∗ ∑ k i sum=\sum d_i=x*\sum k_i sum=di=xki,我们发现 x x x是数组和的一个因子,由于 s u m ≤ 1 0 11 sum\le10^{11} sum1011,计算得到它的因子数大约 4000 4000 4000,因此我们可以考虑枚举 x x x
接下来我们考虑对于每个因子 x x x,如果前 i i i个数的和 s u m i sum_i sumi和前 j j j个数的和 s u m j sum_j sumj满足: s u m i % x = s u m j % x ⇒ ( s u m j − s u m i ) % x = 0 sum_i\%x=sum_j\%x\Rightarrow (sum_j-sum_i)\%x=0 sumi%x=sumj%x(sumjsumi)%x=0,因此区间 [ i + 1 , j ] [i+1,j] [i+1,j]的和是 x x x的倍数,那么由于是个环,我们可以知道剩余的数的和也是 x x x的倍数
那么如果对于某个因子 x x x,我们要得到尽可能多的区间个数 t o t tot tot,那显然我们需要统计前缀和 s u m i % x sum_i\%x sumi%x等于哪个数时的个数最多,最多的个数即为我们对于这个因数 x x x所能划分的最多区间个数 t o t tot tot,显然区间个数小于 t o t tot tot时这个因数 x x x都是符合的
那我们要求每个 k k k对应的最大公约数,只需要求个后缀最大即可

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl '\n';
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e6+10,mod=1e9+7;
ll n,a[N],sum[N],ans[N];
void solve(ll x){
	ll tt=0;
	map<ll,ll>mp;
	for(int i=1;i<=n;++i){
		mp[sum[i]%x]++;
		tt=max(tt,mp[sum[i]%x]);
	}
	ans[tt]=max(ans[tt],x);
}
int main(){
	IOS;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
	int len=sqrt(sum[n]);
	for(int i=1;i<=len;i++){
		if(sum[n]%i==0){
			solve(i);
			solve(sum[n]/i);
		}
	}
	for(int i=n-1;i>0;i--) ans[i]=max(ans[i],ans[i+1]);
	for(int i=1;i<=n;++i) cout<<ans[i]<<endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学不会数据库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值