思路
答案 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 n−1段的最大价值,那么为什么不用 n n n段中的2段看做 1 1 1段,这样就变成 n − 1 n-1 n−1段了,故后利用后缀数组更新一下答案
当为环时 规定
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;
}
}