【JZOJ4922】【NOIP2017提高组模拟12.17】环 题目描述 数据范围 =w= 代码 =o=
小A有一个环,环上有n个正整数。他有特殊的能力,能将环切成k段,每段包含一个或者多个数字。对于一个切分方案,小A将以如下方式计算优美程度:
首先对于每一段,求出他们的数字和。然后对于每段的和,求出他们的最大公约数,即为优美程度。
他想通过合理地使用他的特殊能力,使得切分方案的优美程度最大。
数据范围
对于100%的数据,n<=2000,1<=ai<=50000000(5e7)
=w=
设,
那么答案一定是的约数。
证明:
如果存在一个答案的倍数;
每一段的和(也就是的倍数。
矛盾。
如果切分。
证明:
通过子段合并,显然可得。
于是我们得出思路:
枚举。
代码
// DO NOT FORGET TO OPEN LL
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#define ll long long
using namespace std;
const char* fin="aP1.in";
const char* fout="aP1.out";
const ll inf=0x7fffffff;
const ll maxn=4007,maxp=100000,maxh=99997;
ll n,m,i,j,k;
ll a[maxn],w[maxn],ans[maxn],pre[maxn],sum[maxn];
ll id[maxn];
ll h[maxh],cnt[maxh];
ll getsum(ll l,ll r){
return pre[r]-pre[l-1];
}
ll hash(ll v){
ll k=v%maxh;
while (h[k] && h[k]!=v) k=(k+1)%maxh;
return k;
}
void add(ll x){
ll i=hash(sum[x]+1);
id[x]=i;
if (h[i]==0){
h[i]=sum[x]+1;
cnt[i]=1;
}else cnt[i]++;
}
void del(ll x){
ll i=id[x];
cnt[i]--;
if (!cnt[i]) h[i]=0;
}
ll find(ll x){
ll i=id[x];
if (h[i]) return cnt[i];
else return 0;
}
void solve(ll v){
ll i,j,k,cnt=0,tmp=0;
sum[0]=0;
for (i=1;i<=n;i++) sum[i]=(sum[i-1]+a[i])%v;
for (i=1;i<=n/2;i++) add(i);
for (i=n/2+1;i<=n;i++){
//tong[sum[i-n/2]]--;
del(i-n/2);
add(i);
if (sum[i]==sum[i-n/2]){
tmp=max(find(i-n/2),tmp);
}
}
w[tmp]=max(w[tmp],v);
for (i=n/2+1;i<=n;i++) del(i);
}
int main(){
scanf("%lld",&n);
for (i=1;i<=n;i++) scanf("%lld",&a[i]),m+=a[i];
for (i=n+1,j=n*2;i<=j;i++) a[i]=a[i-n];
for (i=1;i<=n;i++) pre[i]=a[i]+pre[i-1];
n=n*2;
for (i=1,j=(ll)sqrt(m);i<=j;i++){
if (m%i==0) solve(i),solve(m/i);
}
k=1;
for (i=n/2;i>0;i--){
k=max(k,w[i]);
ans[i]=k;
}
for (i=1;i<=n/2;i++) printf("%lld
",ans[i]);
return 0;
}
=o=
其实优化容易想到。
我没深入思考= =