P4734 [BalticOI 2015] Hacker

题目大意

详细题目传送门

思路

对于这种题目一般可以先断环成链。

发现先手所得到的值是一个长度为 \(\lceil \frac{n}{2}\rceil\) 的区间,我们希望让它的元素之和能取到最大,但发现后手会让我们取不到最大。

假设我们从第 \(i\) 台电脑开始,那么后手一定会让我们取到一个所有经过这个点的区间之和的最小值。所以我们只要让这个最小值最大即可。

对于做法先做一个前缀和,之后用 ST 表记录从这个点开始的长度为 \(\lceil \frac{n}{2}\rceil\) 的区间元素之和,然后枚举每一个电脑 \(i\),能够经过它的区间起始位置在 \([i-\lceil \frac{n}{2}\rceil+1,i]\) 之间。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=1e6+5;
ll n,a[MAXN],f[MAXN];
ll st[MAXN][20],lg[MAXN];
ll query(ll l,ll r){
	ll len=lg[r-l+1];
	return min(st[l][len],st[r-(1<<len)+1][len]);
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		a[n+i]=a[i];
	}
	for(int i=1;i<=2*n;++i){
		f[i]=f[i-1]+a[i];
	}
	ll sa=ceil(n*1.0/2);
	for(int i=2;i<MAXN;++i){
		lg[i]=lg[i>>1]+1;
	}
	for(int i=1;i<=2*n;++i){
		st[i][0]=f[i+sa-1]-f[i-1];
	}
	for(int l=1;l<=lg[2*n];++l){
		for(int i=1;i+(1<<l)-1<=2*n;++i){
			st[i][l]=min(st[i][l-1],st[i+(1<<(l-1))][l-1]);
		}
	}
	ll ans=0;
	for(int i=sa;i<=2*n;++i){
		ans=max(ans,query(i-sa+1,i));
	}
	cout<<ans<<endl;
	return 0;
}