【题解】洛谷 P2599 [ZJOI2009] 取石子游戏

· · 题解

前言

本文将在yybyyb 的题解的基础上对证明进行一些必要的补充。

旨在修正一些瑕疵 & 方便读者理解。

复读一遍:

神仙题.jpg。ZJOI 是真的神仙。
发现 SG 函数等东西完全找不到规律,无奈只能翻题解。

话说洛谷 Markdown 的缩进好丑啊……

正文

状态的定义

L(i,j) 表示在 [i,j] 区间的左侧放上一堆数量为 L(i,j) 的石子后,先手必败。(L(i,j) 可以为 0
即:(L(i,j),a_i,a_{i+1},\cdots,a_j) 为必败局面。

同理对右侧定义 R(i,j)

  1. 假设不存在满足定义的 $L(i,j)$,则对于任意非负整数 $x$,$(x,a_i,a_{i+1},\cdots,a_j)$ (记为 $A(x)$ 局面)为必胜局面。 由于 $A(x)$ 为必胜局面,故从 $A(x)$ 局面一步可达某个必败局面。 若拿最左边一堆,则不可能变成必败局面,因为这样得到的局面仍形如 $A$。(注意包括此行在内的接下来几行默认 $x \neq 0$) 于是设 $A(x)$ 一步可达的(某个)必败局面为 $(x,a_i,a_{i+1},\cdots,a_{j-1},y)$,显然有 $0 \le y < a_j$。 **由于 $x$ 有无限个,但 $y$ 只有 $a_j$ 种——根据抽屉原理,必存在 $x_1,x_2(x_1 \neq x_2),y$ 满足 $(x_1,a_i,a_{i+1},\cdots,a_{j-1},y)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},y)$ 都是必败局面。**但这两个必败局面之间实际一步可达,故矛盾,进而原命题成立。
  2. 假设 $L(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$, $(x_1,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面。而这两个必败局面之间实际一步可达,故矛盾,进而原命题成立。

有了唯一性,我们可以自然地得出一个有用的结论:

对于任意非负整数 x \neq L(i,j)(x,a_i,a_{i+1},\cdots,a_j) 为必胜局面。

注:下文所说的“根据 L(\cdots)R(\cdots) 的定义”即指 L(i,j) 的定义 & 这个结论

状态的转移(求解)

边界情况:L(i,i)=a_i。(对于两堆相同的石子,后手一定可以进行和先手对称的操作)

(为方便叙述,下文记 $L(i,j-1)$ 为 $L$,记 $R(i,j-1)$ 为 $R$,并令 $x=a_j(x>0)$) 先提一个后文要用的简单结论(请将它默默记在心中): 若 $R=0$ 则 $L=R=0$,此时 $x>\max\{L,R\}$,也就是说 **$L=0$ 和 $R=0$ 都属于 Case 5,故其它 Case 满足 $L,R>0$**。 - $x=R$(**Case 1**) 最简单的情况——根据 $R(i,j-1)$ 的定义,区间 $[i,j]$ 本来就是必败局面,故 $L(i,j)=0$。 - $x<R

综上所述:

0, \quad &x=R,\\ x+1, \quad &L \le x < R,\\ x-1, \quad &R < x \le L,\\ x, \quad &\text{otherwise}.\\ \end{cases}

温馨提示:请看清楚 L 取不取等,乱取等是错的!

同理可求 R(i,j)

回到原题,先手必败当且仅当 L(2,n)=a_1,于是我们就做完啦!

时间复杂度 O(n^2)

代码

#include<bits/stdc++.h>
using namespace std;
const int max_n=1e3+5;
int a[max_n],L[max_n][max_n],R[max_n][max_n];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        {
            scanf("%d",a+i);
            L[i][i]=R[i][i]=a[i];
        }
        for(int len=2;len<=n;++len)
            for(int i=1;i+len-1<=n;++i)
            {
                int j=i+len-1,l=L[i][j-1],r=R[i][j-1],x=a[j];
                if(x==r)
                    L[i][j]=0;
                else if(x>=l&&x<r)
                    L[i][j]=x+1;
                else if(x>r&&x<=l)
                    L[i][j]=x-1;
                else
                    L[i][j]=x;
                l=L[i+1][j],r=R[i+1][j],x=a[i];
                if(x==l)
                    R[i][j]=0;
                else if(x>=r&&x<l)
                    R[i][j]=x+1;
                else if(x>l&&x<=r)
                    R[i][j]=x-1;
                else
                    R[i][j]=x;
            }
        puts(L[2][n]==a[1]?"0":"1");
    }
    return 0;
}