【JZOJ4922】环

Description

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

Solution

这题很显然答案一定是 ni=1ai 约数。

然后我们就可以愉快的枚举约数,接着我们考虑怎么分段。

不在环上的做法,我们设枚举的约数为 d ,那么我们做一次mod p意义下的前缀和 Si ,然后对于 SiSj=0 的,说明 (i,j] 是可以分段的区间,那么我们可以开一个哈希表或排序记录前面与它相等的位置,得出很多的区间,于是我们再次用O(n)时间合并区间即可。

那么环上是不是就要考虑把头尾区间合并呢?

我们发现其实并不需要,因为在环上,我们显然可以将前面多余的一段补到后面去,那么分段就是完整的了。抽象来说,你现在要在序列中间放隔板,因为在环上,所以放了多少个隔板就有多少段。

最后答案取个后缀最大值即可。

1011 以内的约数不超过5000个。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 2010
#define mo 4000007
#define ll long long
using namespace std;
ll f[N][N];
int a[N],b[N];
ll c[N];
ll ys[N*10];
ll h[mo],zz[mo];
int n;
ll hash(ll x)
{
    ll p=x%mo;
    while(h[p] && h[p]!=x) p=(p+1)%mo;
    return p;
}
int g[mo];
void work(ll x)
{
    ll s=0,z=0;
    g[0]=0;
    fo(i,1,n)
    {
        s=(s+a[i])%x;
        ll p=hash(s);
        g[++g[0]]=p;
        h[p]=s;
        zz[p]++;
        z=max(z,zz[p]);
    }
    c[z]=max(c[z],x);
    fo(i,1,g[0]) h[g[i]]=zz[g[i]]=0;
}
int main()
{
    scanf("%d",&n);
    ll ss=0;
    fo(i,1,n) scanf("%d",&a[i]),ss+=a[i];
    for(ll i=1;i*i<=ss;i++)
    if(ss%i==0)
    {
        ys[++ys[0]]=i;
        if(ss/i!=i) ys[++ys[0]]=ss/i;
    }
    fo(i,1,ys[0]) work(ys[i]);
    fd(i,n-1,1) c[i]=max(c[i],c[i+1]);
    fo(i,1,n) printf("%lld\n",c[i]);
}
posted @ 2016-12-19 20:37  sadstone  阅读(51)  评论(0编辑  收藏  举报