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;
}