【Codeforces Round #645 (Div. 2) E】 Are You Fired?
【题目翻译】
给你一个长度为n的序列a,但是只给你前[n/2](向上取整)个数字,然后后面[n/2](向下取整)个数字都是x,现在让你求一个数字k,使得序列a中每个长度为k的连续序列的和都大于0.
【题解】
我们先证,如果存在一个满足要求的k的话,那么k2=2*k肯定也是一个符合要求的答案。为什么?
我们可以假设s[i]=a[i]+a[i+1]+a[i+2]+...+a[i+k-1]
则因为k满足要求,所以对于任意一个s[i],都有s[i]>0
那么对于k2=2*k,同样定义个大号版的S[i] = s[i] + s[i+k]
因为每个s[i]都大于0,则S[i]肯定也是大于0的。
所以如果最后有答案的话,肯定有一个大于等于n/2(向上取整)的满足要求的k.
这就很有用了。
仍然是上面定义的si
我们看一下s[i+1]和s[i]的关系。会发现s[i+1]=s[i]+a[i+k]-a[i]
这是不是有点类似前缀和的样子,实际上会发现s[i]就是由这么一个数组的前缀和
即p={s1,a[1+k]-a[1],a[2+k]-a[2],......,a[n]-a[n-k]}
这玩意有啥用呢?其实就是说 如果我们能够让某个k,使得对应的这个数组p的每个前缀和都大于0,那么我们就找到了答案。
这个p现在有点复杂,但是想想题目给我们的条件?后半部分都是x,所以a[1+k],a[2+k]...都是x呀!
那p={s1,x-a[1],x-a[2],....,x-a[n-k]}
但是我们要怎么找到这么合适的一个k呢,肯定是枚举了(大于等于n/2嘛),所以我们考虑一下如果k+1了,这个p数组会变成啥样。
即p={s1+x,a[2+k]-a[1],a[3+k]-a[2],....a[n]-a[n-k-1]},然后把x代入。
p={s1+x,x-a[1],x-a[2],...,x-a[n-k-1]}。所以这个p数组,实际上就只是减少了一个元素,然后第一位加上了x。
时刻注意 我们只关注这个数组的前缀和的里面的最小值。
而s[i]=p[1]+p[2]+p[3]+...+p[i],即
s[]={p1,p1+p2,p1+p2+p3,....,p1+p2+p3+...+p[n-k+1]}
既然第一个元素很麻烦,那我们就把每个s[i]都去掉p[1],则我们得到一个新的s数组(算答案的时候加上p1就行,不影响正常的求最小值)
s[]={0,p2,p2+p3,...,p2+p3+...+p[n-k]};
会发现,对于不同的k,这个数组都是一样的!(就是k越大,长度会减小1).
那么我们就可以求出来一个m[]数组,这个m数组中m[i] = min{s[1],s[2]...s[i]} (这个很好求的,用个变量累加x-a[i],然后求最小就好)。
这样,对于我们枚举的K,直接看p1+mn-K+1是不是大于0就好了。
如果不是的话,p1递增x(因为k变大1了)继续判断。
【代码】
#include<bits/stdc++.h>
#define ll long long
#define rei(x) scanf("%d",&x)
#define rel(x) scanf("%I64d",&x)
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
using namespace std;
const int N = 50e4;
int T,n,nn,x;
int a[N+10];
ll m[N+10];
int main(){
//cout<<(1LL<<62)<<endl;
#ifdef LOCAL_DEFINE
freopen("D:\\rush.txt","r",stdin);
#endif
ios::sync_with_stdio(0),cin.tie(0);
cin >> n;
nn = (n+1)/2;
rep1(i,1,nn) cin >> a[i];
cin >> x;
m[1] = 0;
ll sum = 0;
rep1(i,2,n-nn+1){
sum += x-a[i-1];
m[i] = min(m[i-1],sum);
}
sum = 0;
rep1(i,1,nn) sum+=a[i];
int ans = -1;
rep1(k,nn,n){
if (sum+m[n-k+1]>0){
ans = k;
break;
}
sum+=x;
}
cout<<ans<<endl;
return 0;
}