【BZOJ2000】[HNOI2000]取石头游戏(贪心,博弈论)

【BZOJ2000】[HNOI2000]取石头游戏(贪心,博弈论)

题面

BZOJ
洛谷

题解

这题好神仙啊,窝不会QaQ。
假装一下只有三个元素\(a_{i-1},a_i,a_{i+1}\),并且满足,\(a_{i-1}\le a_i\ge a_{i+1}\)那么肯定是\(a_{i-1}+a_{i+1}\)\(a_i\)这样子分配的。那么两个人的差就是\(a_{i-1}+a_{i+1}-a_i\),那么我们把\(i\)和旁边两个元素直接合并就好了,反正只要知道了两个人的差和所有元素之和就能还原答案。
不难发现这样子合并完之后序列要么单增要么单减。
我们发现中间被分开的一段段是一个双端队列,可以从两端取。两侧被分割的部分是一个栈,只能一侧取。显然两侧的按照奇偶可以直接分配好谁去哪一侧。而剩下的部分因为单调,所以显然排序之后两个人一个个轮流取就好了。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define MAX 1000100
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,l,r,fr,top;ll S[MAX],sum,ans;bool vis[MAX];
bool check(int p){if(vis[p]||vis[p-1]||vis[p+1])return false;return S[p-1]<=S[p]&&S[p]>=S[p+1];}
int main()
{
	n=read();
	for(int i=1;i<=n;++i)
	{
		S[++top]=read();sum+=S[top];vis[top]=(S[top]==0);fr^=(bool)(S[top]);
		while(top>=3&&check(top-1))S[top-2]=S[top-2]+S[top]-S[top-1],top-=2;
	}
	for(l=1;!vis[l]&&!vis[l+1]&&S[l]>=S[l+1];l+=2)ans+=(S[l]-S[l+1])*(fr?1:-1);
	for(r=top;!vis[r]&&!vis[r-1]&&S[r]>=S[r-1];r-=2)ans+=(S[r]-S[r-1])*(fr?1:-1);
	top=0;for(int i=l;i<=r;++i)if(!vis[i])S[++top]=S[i];sort(&S[1],&S[top+1]);
	for(int i=top;i;--i)ans+=((top-i)&1)?-S[i]:S[i];
	cout<<(sum+ans)/2<<' '<<(sum-ans)/2<<endl;
	return 0;
}
posted @ 2018-10-25 20:24  小蒟蒻yyb  阅读(441)  评论(0编辑  收藏  举报