Loading

P2599 [ZJOI2009]取石子游戏 做题感想

题目链接

前言

发现自己三岁时的题目都不会做。
我发现我真的是菜得真实。

正文

神仙构造,分讨题。
不敢说有构造,但是分讨我只服这道题。

看上去像是一个类似 \(Nim\) 游戏的变种,经过不断猜测结论无果后果断弃疗。
(然后我就出门右转直接进了题解区),在这里记录一下自己的理解。

\(l_{i,j}\) 表示在 \(i\sim j\) 这个区间左边再加一堆 \(l_{i,j}\) 的石子时,先手必败。
同理,设 \(r_{i,j}\) 表示在 \(i\sim j\) 这个区间右边再加一堆 \(r_{i,j}\) 的石子时,先手必败。

(以下的所有证明以及验证我们都以 \(l_{i,j}\) 为准,\(r_{i,j}\) 情况相同)。

等等,如果上述满足条件的石子个数不唯一,怎么办?
好像不太好搞欸,是不是要再开一维数组?
既然不唯一的情况这么困难,为什么不想想会不会存在这种情况呢?

证明:
我们假设 \(l_{i,j}\) 存在两种可能 \(a\)\(b\) ,且我们钦定 \(a < b\)
既然 \(a\)\(b\) 都满足要求,所以此时都是必败态。
但是很显然,我们可以让先手对于 \(b\) 的情况一直取直到 \(a\)
此时先手让后手到了一个必败态所以先手必败。??(对,这就是不唯一时的推理)
所以可以得出,\(l_{i,j}\) 一定是唯一的。

那万一 \(l_{i,j}\) 不存在怎么办?
等一等,\(l_{i,j}\) 是不是一定存在呢?
在这里我口胡一下:

因为对于所有的 \([i,j]\) 区间都不存在 \(l_{i,j}\) ,则对于所有从左边拿的一定是必胜态。
但是如果只有一堆石子 \(a\) 的话:此时可以发现 \(l_{i,j} = a\) 因为此时先手怎么取后手也学者他。
这样的话后手会取完最后一个石子。
所以,\(l_{i,j}\) 一定是存在的的。


现在来进行刺激的分讨过程。
我在这里会对每一种可能的结果进行简要的说明。

  • 首先我们先来考虑边界的情况:
    就和我之前说的一样 \(l_{i,i} = a_i\)\(r_{i,i}\) 同理。

为了方便起见,我们令 \(x=a_j\)\(L =l_{i,j-1}\)\(R=r_{i,j-1}\)

  • \(x < L\)\(x <R\) ) 或者 ( \(x > L\)\(x >R\)
    \(l_{i,j} = x\)

此时我们可以运用类似之前的方法,先手取什么,后手就取什么。
那么最后的结果就是先手先取完了左右两端石子中的一堆。
然后后手可以随便取另一边的一堆,使得此堆的数量变成 \(l_{i,j}\)\(r_{i,j}\)

  • \(R<x<L\)
    \(L_{i,j} = x-1\)

假设先手先拿了左边这一堆。
那么假设还剩下了 \(x\) 个石子,如果 \(x<R\),后手把右侧的那一堆也给拿成 \(x\) 就成了( \(x < L\)\(x <R\) )这种情况。
如果 \(x\geq R\),那么后手把最后那一堆拿成 \(x+1\),于是又回到了我们讨论的这种情况。
同理,我们也可以推出右边先取玩的的情况。

  • \(L<x<R\)
    \(L_{i,j} = x+1\)
    和上一种基本上是一摸一样,在这里就不讲了。

Code

#include <bits/stdc++.h>

#define file(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)

#define Enter puchar('\n')
#define quad putchar(' ')

const int N = 1005;

int T, a[N], l[N][N], r[N][N];

signed main(void) {
//  file("P2599");
  std::cin >> T;
  for (int test = 1, n; test <= T; test++) {
    std::cin >> n;
    for (int i = 1; i <= n; i++)
      scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++)
      l[i][i] = r[i][i] = a[i];
    for (int len = 1; len <= n; len ++) {
      for (int i = 1; i + len - 1 <= n; i++) {
        int j = i + len - 1;
        int L, R, x;
        x = a[j];
        L = l[i][j - 1]; R = r[i][j - 1];
        if (R == x) l[i][j] = 0;
        else if (x < L && x < R) l[i][j] = x;
        else if (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 (L == x) r[i][j] = 0;
        else if (x < L && x < R) r[i][j] = x;
        else if (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;
      }
    }
    if (l[2][n] == a[1]) std::cout << "0" << std::endl;
    else std::cout << "1" << std::endl;
  }
}
posted @ 2022-04-19 09:59  Aonynation  阅读(54)  评论(0编辑  收藏  举报