AGC026F Manju Game

两个人博弈:一排格子,每个格子有一定的价值\(a_i\)。操作:如果上一次操作的位置旁边有空位,选择其中一个空位占据;否则任选一个空位。

每个人最大化自己的价值和。输出结果。

\(n\le 3*10^5\)


博弈吼题。

先手一定不希望在一堆操作之后被占据先机:

稍微举例说明。

假如\(2|n\),如果先手选了\(1\),那么选择的情况为:\(0,1,0,1,\dots,0,1\)。(记为情况\(A\)

先手选了\(x\)\([1,x]\)为奇数区间,\([x,n]\)为偶数区间。

如果后手选了\([x,n]\)(记为情况\(B\)),则先后手不会调换,\([x,n]\)区间中选择情况为\(0,1,\dots,0,1\),与情况\(A\)\([x,n]\)段相同。剩下了个\([1,x]\)的子问题。因为先机在先手,所以先手不会让它比\(A\)更劣。所以对于先手来说,情况\(B\)优于情况\(A\)

如果先手选了\([1,x]\)(记为情况\(C\)),则先后手调换,\([1,x]\)区间中选择情况与情况\(A\)\([1,x]\)段相同,剩下了个\([x,n]\)的子问题。因为先机变了,所以后手不会让它比\(A\)更优。所以对于先手来说,情况\(C\)劣于情况\(A\)

综上,此时后手一定会选\(C\),因为\(C\)劣于情况\(A\),所以先手会选\(A\)

这表明了\(2|n\)时可以直接做。同时稍微扩展一下可以得出先手一定不会把先机给对方。

考虑\(n\)为奇数的情况。大概过程:先手选择一个偶数位,后手选其中一边,变成子问题继续;直到某一次先手直接选择最边,结束。

假如结束时的这个区间为\([l,r]\),那么先手得到的贡献为:\([1,l-1]中的偶数和+[l,r]中的奇数和+[r+1,n]中的偶数和\)

于是先手希望最大化\([l,r]中的奇数和-[l,r]中的偶数和\)。二分这个值\(lim\),判定:找到若干个区间满足\([l,r]中的奇数和-[l,r]中的偶数和\ge lim\),这些区间满足:\(r_i+1=l_{i+1}-1\)\(2|r_i+1\)。简单DP实现。

(先手可以钦定分界点,后手最小化这个值,所以就将序列以偶数位置分成若干个区间,让权值最小的区间最大)


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 300005
#define INF 300000000
int n;
int a[N];
int s[N];
int f[N];
bool judge(int lim){
	int mn=INF;	
	for (int i=1;i<=n;i+=2){
		f[i]=(s[i]>=lim);
//		for (int j=2;j<i && !f[i];j+=2)
//			if (f[j-1] && s[i]-s[j]>=lim)
//				f[i]=1;
		if (s[i]-lim>=mn)
			f[i]=1;
		if (f[i])
			mn=min(mn,s[i+1]);
	}
	return f[n];
}
void print(int ms){
	int ad=0;
	for (int i=1;i<=n;++i)
		ad+=a[i];
	printf("%d %d\n",ad+ms>>1,ad-ms>>1);
}
int main(){
	freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for (int i=1;i<=n;++i)
		scanf("%d",&a[i]);
	if (!(n&1)){
		int ans=0;
		for (int i=1;i<=n;i+=2)
			ans+=a[i]-a[i+1];
		print(max(ans,-ans));
		return 0;
	}
	for (int i=1;i<=n;++i)
		s[i]=s[i-1]+(i&1?1:-1)*a[i];
	int l=-INF,r=INF,res=-INF;
	while (l<=r){
		int mid=l+r>>1;
		if (judge(mid))
			l=(res=mid)+1;
		else
			r=mid-1;
	}
	print(-s[n]+res*2);
	return 0;
}
posted @ 2020-10-23 21:36  jz_597  阅读(181)  评论(0编辑  收藏  举报