Luogu3210 [HNOI2010]取石头游戏
https://www.luogu.com.cn/problem/P3210
贪心
我们维护先手得分与后手得分的差值
结论:
在任何情况下,如果能够取到整个序列的最大值,那么取到整个序列的最大值一定最优
证明:
假设有一个对手,与你同时下同一种局面下的两个游戏
在第一局中,对手执先手,在第二种局面中,你执先手
你的策略是上述结论,你只需要保证第二种局面不会比第一种更劣就好了
\(Round 1\)
第一局:
对手\(p_1\):\(x\)
你\(q_1\):\(Max\)
第二局:
你\(q_2\):\(Max\)
对手\(p_2\):\(y\)
接下来的几轮,你就模仿对手就好了,对手取啥,你就取啥
有时你取不到对手取的东西,你就取对手取的位置旁边的\(0\)的同一方向的第一个位置就好了
这样保证满足:\(q_2=p_1-\{x^{'}\}+\{M\},q_1=p_2-\{y^{'}\}+\{M\}\)(注:\(x^{'},y^{'}\)不一定是\(x,y\))
观察上面的式子,你惊奇地发现你总是更优
一旦在一轮两局中出现相同的局面,就可以直接结束(你可以完全模仿对方啦!)
证明完毕!
但是\(Max\)不一定能够取到,我们先考虑一下简化序列
对于序列\(x,y,z(x<y,z<y)\),先后手肯定都想取\(z\),所以没人会先取,除非不得已
如果一定要取,一定是不得已了,那么一定会一口气取完,先取的产生贡献\(x+z-y\)
这样我们可以把所有序列(中间用\(0\)隔开)简化成单调递减、单调递增或凹形的序列
对于不在两端的序列,我们随时可以取得它的最大值,一个个取就是最优解
对于首尾的序列,对于首单调递增的那部分,尾单调递减的那部分,我们可以按照上面的方法处理
麻烦的是剩余的那部分(这里包括山谷),考虑一下,会不会在不在两端的序列还没取完的时候,就有人去取它了呢?
假设先手取了,那么说明在如果最后自动分配时,这个数是后手取的(对先手不利)
那么显然,后手不会继续取(隔一个取的)
根据奇偶性,后手依旧可以取到相同奇偶性的较优秀的方案
所以,我们只需要把山谷也当成可以直接取的来处理就好了
\(OK!\)
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#define N 1000005
#define _INF -123456789987654321
#define ll long long
using namespace std;
int cnt,_n,n,L,R,RL,RR;
ll _s,s=0,st,ans=0,x,a[N];
priority_queue<ll>q;
int main()
{
scanf("%d",&_n);
L=_n+1,R=0;
for (int i=1;i<=_n;i++)
{
scanf("%lld",&x);
s+=x;
if (!x)
{
a[++n]=_INF;
continue;
}
a[++n]=x;
while (n>2 && a[n-1]!=_INF && a[n-2]!=_INF && a[n-2]<a[n-1] && a[n-1]>a[n])
{
a[n-2]=a[n-2]+a[n]-a[n-1];
n-=2;
}
}
for (int i=1;i<=n;i++)
if (a[i]!=_INF)
cnt++;
for (L=1;a[L]!=_INF && a[L+1]!=_INF && a[L]>=a[L+1];L+=2)
{
if (cnt & 1)
ans+=a[L]-a[L+1]; else
ans+=a[L+1]-a[L];
}
for (R=n;a[R]!=_INF && a[R-1]!=_INF && a[R]>=a[R-1];R-=2)
{
if (cnt & 1)
ans+=a[R]-a[R-1]; else
ans+=a[R-1]-a[R];
}
for (;L<=R;L++)
if (a[L]!=_INF)
q.push(a[L]);
st=1;
while (!q.empty())
{
ans+=st*q.top();
q.pop();
st=-st;
}
printf("%lld %lld\n",(s+ans) >> 1,(s-ans) >> 1);
return 0;
}