题解 P3210 【[HNOI2010]取石头游戏】
感谢巨捞YCB orz
部分内容来自GuessYCB博客
题目的意思是:给两个栈和一些双端队列,问先后手最优方案下的取值。
因为总价值已经确定,那么先手的目标是使得自己与对方的差值尽可能大,后手的目标时尽可能小
先看栈,栈的取值其实是固定了的。
对于每一个栈,如果是单调下降
例如左边的栈 若有ai<ai-1<ai-2
那么当前取先手一定不是最优
因为后手取到的ai一定大于ai-1
如果对于栈单调上升
那么当前取后手一定不是最优
对于没有单调关系的变化
如果是ai-1<ai>ai+1这种情况
那么此时的先手与后手的收益差其实是确定了的
先手先取旁边两个点中的一个,后手一定取中间最优
先手再取另外一个
这样的取法对于中间的队列可行,对于两边的栈也是可行的
那么我们可以将三个合并为一个
即anew=ai−1+ai+1−ai
这样把所有的上凸关系全部合并完之后,大概整个图长这个样子
每一块一定是开口向上的二次函数形状
对于中间的队列,最优一定可以立刻取到
为什么呢,因为两边最大中间最小,当前先手一定会取两边,而两边又是合法的
对于左右两个栈的靠近里面的段也是合法的情况
那么对于这些石头排序后贪心选就可以了
对于栈单调靠近外面的段,进行相邻两两配对
无法配对的靠内单项和之前的石头一起排序
这样保证是最优的
因为没有人愿意第一个开始选那两段
所以在中间全部选完后,判断奇偶性直接算贡献就好了
附代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#define il inline
#define rg register
#define ll long long
#define ld long double
#define inf 2147483647000
#define mod 998244353
#define N 1000001
using namespace std;
ll n,op,Qcnt,Gcnt;
ll tot,now,fir,ans,w[N],Q[N],G[N];
ll A[N],Acnt,B[N],Bcnt;
il void re(rg int &x);
il void Re(rg ll &x);
int main(){
Re(n),G[0]=-inf;
for(rg int i=1;i<=n;++i)
Re(w[i]),tot+=w[i];
for(rg int i=1;i<=n;++i){
if(w[i]){
G[++Gcnt]=w[i],w[i]=0;
if(Gcnt>=3)
while(G[Gcnt]<=G[Gcnt-1]&&G[Gcnt-1]>=G[Gcnt-2]){
G[Gcnt-2]=G[Gcnt]+G[Gcnt-2]-G[Gcnt-1];
Gcnt-=2;
if(Gcnt<=2)break;
}
}
else break;
}
for(op=Gcnt;op>=1;--op){
if(G[op]>=G[op-1])
Q[++Qcnt]=G[op];
else break;
}
for(rg int i=1;i+1<=op;i+=2)
A[++Acnt]+=G[i+1]-G[i];
if(op&1)Q[++Qcnt]=G[op];
//预处理前面
Gcnt=0;
for(rg int i=n;i>=1;--i){
if(w[i]){
G[++Gcnt]=w[i],w[i]=0;
if(Gcnt>=3)
while(G[Gcnt]<=G[Gcnt-1]&&G[Gcnt-1]>=G[Gcnt-2]){
G[Gcnt-2]=G[Gcnt]+G[Gcnt-2]-G[Gcnt-1];
Gcnt-=2;
if(Gcnt<=2)break;
}
}
else break;
}
for(op=Gcnt;op>=1;--op){
if(G[op]>=G[op-1])
Q[++Qcnt]=G[op];
else break;
}
for(rg int i=1;i+1<=op;i+=2)
B[++Bcnt]+=G[i+1]-G[i];
if(op&1)Q[++Qcnt]=G[op];
//预处理后面
Gcnt=0;
for(rg int i=1;i<=n;++i){
if(w[i]){
G[++Gcnt]=w[i];
if(Gcnt>=3)
while(G[Gcnt]<=G[Gcnt-1]&&G[Gcnt-1]>=G[Gcnt-2]){
G[Gcnt-2]=G[Gcnt]+G[Gcnt-2]-G[Gcnt-1];
Gcnt-=2;
if(Gcnt<=2)break;
}
}
else while(Gcnt)Q[++Qcnt]=G[Gcnt--];
}
sort(Q+1,Q+Qcnt+1),op=1;
for(rg int i=Qcnt;i>=1;--i)
ans+=op*Q[i],op*=-1;
ll AA=0,BB=0;
for(rg int i=1;i<=Acnt;++i)
AA+=A[i];//,cout<<A[i]<<' ';
for(rg int i=1;i<=Bcnt;++i)
BB+=B[i];//,cout<<B[i]<<' ';
// cout<<endl;
// cout<<ans<<' '<<AA<<' '<<BB<<endl;
if(Qcnt&1)ans-=(AA+BB);
else ans+=(AA+BB);
printf("%lld %lld\n",(tot+ans)>>1,(tot-ans)>>1);
return 0;
}
il void re(rg int &x){
x=0;rg int w=1;char c=getchar();
while((c<'0'||c>'9')&&c!='-')c=getchar();
if(c=='-')w=-1,c=getchar();
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
x*=w;
}
il void Re(rg ll &x){
x=0;rg int w=1;char c=getchar();
while((c<'0'||c>'9')&&c!='-')c=getchar();
if(c=='-')w=-1,c=getchar();
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
x*=w;
}