DTOJ3305 环(circle)

题目

题目描述

小A有一个环,环上有 n n n个正整数
他有特殊的能力(???),能将环切成 k k k段,每段包含一个或者多个数字
对于一个切分方案,小A将以如下方式计算优美程度:
首先对于每一段,求出他们的数字和
然后对于每段的和,求出他们的最大公约数,即为优美程度
他想通过合理地使用他的特殊能力,使得切分方案的优美程度最大

输入格式

第一行一个整数 n n n,表示环上的数字个数
接下来一行包含 n n n个正整数,第 i i i个数 a i a_i ai表示环上第 i i i个数

输出格式

输出 n n n行,第 i i i行表示切成 i i i段时的最大优美程度

样例

样例输入

7
2 3 3 3 3 3 3

样例输出

20
5
2
2
1
1
1

数据范围与提示

对于 20 % 20\% 20%的数据, n ⩽ 16 , a i ⩽ 10 n \leqslant 16,a_i \leqslant 10 n16,ai10
对于 40 % 40\% 40%的数据, n ⩽ 100 , a i ⩽ 1000 n \leqslant 100,a_i \leqslant 1000 n100,ai1000
对于 100 % 100\% 100%的数据, n ⩽ 2000 , 1 ⩽ a i ⩽ 5 × 1 0 7 n \leqslant 2000,1 \leqslant a_i \leqslant 5 \times 10^7 n2000,1ai5×107

题解

显然,答案是 ∑ i = 1 n a i \sum \limits_{i=1}^na_i i=1nai的约数
所以我们可以枚举 ∑ i = 1 n a i \sum \limits_{i=1}^na_i i=1nai的每一个约数,对于每一个约数 d d d,计算最多能切成几段,使得这几段 g c d gcd gcd起来是 d d d
现在的问题就是到底能分成几段
这个非常简单
我们统计前缀和 s u m i sum_i sumi,如果 s u m i ≡ s u m j ( m o d d ) sum_i\equiv sum_j\pmod{d} sumisumj(modd),那么 i ∼ j i\sim j ij就可以分一段
所以,我们可以用一个map统计 s u m i sum_i sumi d d d的余数,然后答案就是这个map中值最大的那个
最终的答案就是后缀最大值啦!
附上代码:

#include<cstdio>
#include<cmath>
#include<map>
using namespace std;
int n;
long long a[2010],ans[2010];
map<long long,int> mod;
void work(long long k)
{
	mod.clear();
	for(int i=1,s;i<=n;i++) s=++mod[a[i]%k],ans[s]=max(ans[s],k);
}
int main()
{
	freopen("circle.in","r",stdin);
	freopen("circle.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]),a[i]+=a[i-1];
	for(long long i=1;i<=sqrt(a[n]);i++) if(!(a[n]%i)){
		work(i);
		if(i*i!=a[n]) work(a[n]/i);
	}
	for(int i=n;i;i--) ans[i]=max(ans[i],ans[i+1]);
	for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值