题解 P2599 【[ZJOI2009]取石子游戏】
神仙题.jpg。
发现
首先设
首先我们可以证明
接下来考虑如何证明
综上,我们知道了
现在考虑如何求解
接下来来大力分类讨论,为了方便,设
-
x=R 这种情况下显然只需要直接把
a[j] 放进去就好了,即这个区间本身就是一个必败态。所以L[i][j]=0 。 -
x<L,x<R 这种情况下
L[i][j]=x 。这种情况下最靠左的L[i][j] 和x=a[j] 是相同的,意味着先手无论怎么取,后手显然可以学着它的方法取,也就意味着左右两堆中显然必然会先拿完一堆,此时后手学着拿的那一堆的石子数一定也是小于L,R 的。假设先手先拿完了最靠右的一堆,即剩下了[i,j-1] ,因为L[i][j-1] 表示的是在这一段区间最左侧加入一个L[i][j-1] 的堆,无论先手怎么取先手都是必败的,那么我们等价的认为先手取走了这一堆的一部分,显然后手是必胜的。假如先手先取完的是最左的一堆,同理,R[i][j-1] 的含义是在最右侧加入了一堆,而a[j]<R[i][j-1] ,我们还是可以等价的认为先手在这一堆中取走了若干石子,而这个状态对于先手而言是必败状态,因此显然后手必胜。 -
R<x<L 这种情况下
L[i][j]=x-1 。这样子考虑,假设先手先拿了左边这一堆,那么假设还剩下了z 个石子,如果z<R ,后手把右侧的那一堆也给拿成z 就变成了上面的情况。如果z\ge R ,那么后手把最后那一堆拿成z+1 ,于是又回到了这种情况,相当于这种情况递归处理。如果先手先拿的是右侧的这一堆,还是一样的,假设把它拿成了z ,如果z<R ,同上可以变成x<L,x<R 的情况;如果y=R ,直接把左边拿完,就变成了R[i][j-1] 的定义了,先手必败;如果z>R ,把左边那堆变成z-1 ,同样递归处理。 -
L<x<R 分析同上,
L[i][j]=x+1 。 -
x>L,x>R
而
那么最终只需要判断
代码有点丑
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 1010
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,a[MAX],L[MAX][MAX],R[MAX][MAX];
int main()
{
int T=read();
while(T--)
{
n=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<=n;++i)L[i][i]=R[i][i]=a[i];
for(int len=2;len<=n;++len)
for(int i=1,j=i+len-1;j<=n;++i,++j)
{
int x=a[j],l=L[i][j-1],r=R[i][j-1];
if(x==r)L[i][j]=0;
else if((x>l&&x>r)||(x<l&&x<r))L[i][j]=x;
else if(r<x&&x<l)L[i][j]=x-1;
else L[i][j]=x+1;
x=a[i],l=L[i+1][j],r=R[i+1][j];
if(x==l)R[i][j]=0;
else if((x>l&&x>r)||(x<l&&x<r))R[i][j]=x;
else if(r<x&&x<l)R[i][j]=x+1;
else R[i][j]=x-1;
}
puts(a[1]==L[2][n]?"0":"1");
}
}