Codeforces Round #645 (Div. 2) E. Are You Fired?

题目链接

思路:首先一个前置结论:
如果有答案,则必然有一个 ≥ n + 1 2 \geq \frac{n+1}{2} 2n+1的答案。
因为,如果有个答案k,那么2k也是满足的,不断倍增即可得此结论。
那么对于 [ n + 1 2 , n ] [ \frac{n+1}{2},n] [2n+1,n]的每个数,我们怎么check合法性呢?
设当前check的值为k:
显然对于任意一个 1 ≤ i ≤ n − k + 1 1 \leq i\leq n-k+1 1ink+1,都有 ∑ j = i j + k − 1 a j > 0 \sum_{j=i}^{j+k-1} a_j >0 j=ij+k1aj>0
我们可以计算出第一段长度k的区间的 s u m = ∑ j = 1 k a j sum=\sum_{j=1}^{k} a_j sum=j=1kaj,然后区间每次右移的时候,最左端的点会被去掉,再加上新增的。由于数组后半部分的数都是一样的。所以我们可以知道,每次区间移动的时候,区间和 的变换量。
移动1次的变化为 x − a 1 x-a_1 xa1
移动2次的变化为 x − a 1 + x − a 2 x-a_1 +x-a_2 xa1+xa2
移动3次的变化为 x − a 1 + x − a 2 + x − a 3 x-a_1 +x-a_2 +x-a_3 xa1+xa2+xa3
那么移动p次的变化就是 p ∗ x − ∑ i = 1 p a i p*x-\sum_{i=1}^pa_i pxi=1pai
即我们可以计算出任意长度的区间移动一定次数后的区间和,而我们只需要保证区间和是都要大于0即可。即移动1到n-k次中的最小值要大于0,那么显然维护一个前缀最小值就行。
ps:比赛的时候没想出正解,还憨憨冲了一发bitset。。。。。。

#include 
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
int t,n,m,a[N],f[N];
LL sum[N],mn[N];
int main() {
	scanf("%d",&n);
  for(int i=1;i<=(n+1)/2;i++)scanf("%d",a+i);
  scanf("%d",&m);
	LL x=0,y=0;
	for(int i=1;i<=(n+1)/2;i++){
		x+=m-a[i];
		mn[i]=min(x,mn[i-1]);
	}
	for(int i=1;i<=(n+1)/2;i++){
		y+=a[i];
	}
	for(int i=(n+1)/2;i<=n;i++){
		if(y+mn[n-i]>0){
			return cout<<i<<'\n',0;
		}
		y+=m;
	}
	
 	cout<<-1<<'\n';
 	return 0;
}

你可能感兴趣的:(cf)