今早看到的题,想了下会做了,但是觉得这题挺有意思的,于是打算写一下做法。本题利用了gcd的基本性质:更相减损法以及结合律,平时做gcd的题基本没用到过这两性质,而本题对这性质进行了充分利用。
思路:
首先我们考虑给一个序列,我们该怎么做。
令 fn=∑ni=1ai 。
我们考虑序列的一个 k+1 划分 fx1,fx2−fx1,fx3−fx2,...,fn−fxk ,记为 {x1,x2,x3,...,xk−1,xk} 。
令 A=fx1,B=fx2−fx1,C=fx3−fx2 。
现在我们有 gcd 的两条基本但十分重要的性质:
1. gcd 本质是更相减损法,即 gcd(A,B−A)=gcd(A,B) 。
2. gcd 满足结合律,即 gcd(gcd(A,B),C)=gcd(A,gcd(B,C)) 。
gcd(A,B-A,C-B)
=gcd(gcd(A,B-A),C-B)
=gcd(gcd(A,B),C-B)
=gcd(A,gcd(B,C-B))
=gcd(A,gcd(B,C))
=gcd(A,B,C)
根据数学归纳法,可以得出结论:序列上的任意一个 k+1 划分 {x1,x2,x3,...,xk−1,xk} 等价于 gcd(fx1,fx2,fx3,...,fxk,fn) 。
因为序列的任意一个划分总是包含 fn ,因此答案一定是 fn 的约数。
接下来,考虑枚举 gcd 的值 g (即枚举答案, fn 的约数),即 fn 的约数(不超过 1e4 个),然后计算 fimodg=0 的个数,假设为 x 个,则说明 1 ~ x 的划分的答案均可为此值。
了解了链上的做法,接下来考虑环上的做法。
考虑环上的断点为 n 和 1 之间,记为 0 ,则答案即链上求得的答案。
现在考虑将断点向右移动 x 单位,即 fx 属于最后一个区间,且 1 ~ x 之间不可能再有断点,否则我们在之前便已枚举过。
然后枚举 gcd 的值 g ,然后计算 x+1 ~ n 内满足 fymodg=fxmodg 的 y 的个数。
为了加速这一过程,考虑先枚举 gcd 的值 g ,再枚举断点 x ,然后看断点之后满足 fymodg=fxmodg 的 y 的个数。
其实无需真的枚举断点,换个角度思考,枚举断点等价于枚举 fxmodg 的值,而每个 fxmodg 的值只有编号最小的有用,因为在这样的 x 之后的 y 才会尽可能多。
既然知道是什么原理了,最后总结一下做法:
1.枚举 gcd 的值 g ,
2.令 bi=fimodg ,
3.对 b数组 排序,然后统计相同的值出现的次数,
4.初始化 ans[] ,令值全为1,
5.假设值为 x 的出现了 y 次,则令 ans[y]=max(ans[y],x) ,
6.对 ans 更新得后缀最大值。
for ( int i = n ; i >= 1 ; --i ) {
ans[i-1] = max ( ans[i - 1] , ans[i] ) ;
}
7.第 i 行输出 ansi 。