[CF282D] Yet Another Number Game 题解

[CF282D] Yet Another Number Game

传送门

这题可以分三种情况讨论 \(n\) 的取值。

n = 1

显然,当 \(a1 \neq 0\) 时先手可以一下全部取完,后手必败,否则后手必胜。

n = 2

有一个朴素的 DP。

\(dp[i][j]\) 表示两堆石头分别剩余 \(i\) 个和 \(j\) 个时先手是否必胜,必胜取 \(1\),否则取 \(0\)

\(dp[i][j] |= !dp[i - k][j](1\leq k\leq i)\)

$ dp[i][j] |= !dp[i][j - k](1\leq k\leq j)$

且有 $ dp[i][j]|= !dp[i - k][j - k](1\leq k\leq \min(i, j))$

分别代表三种可能的取法,这是 \(O(A^3)\) 的,其中 \(A\) 表示 \(a_i\) 的最大取值。

n = 3

可以用前缀和优化上一个转移方程,不过这里用博弈论的方法解决。

结论:若 \(a\oplus b\oplus c \ne 0\) 则先手必胜,否则先手必败,其中 \(\oplus\) 表示按位异或,\(a, b, c\) 代表三堆石子的剩余数量,且令 \(a \le b \le c\)

构造一种选法:

如果当前局面 \(a\oplus b\oplus c \ne 0\),则先手一定能找到一种操作使得操作后 \(a\oplus b\oplus c = 0\),换言之 \(a\oplus b = c\)

证明:设 \(a\oplus b = k\),则

  1. \(c > k\),选择操作一使 \(c\) 变为 \(k\) 即可

  2. \(c < k\),则有 \(a\oplus b > c\),则 \(a>b\oplus c\)

    \(b\oplus c = k_2\),此时直接选择操作一使 \(a\) 变为 \(k_2\) 即可

然后到了后手面临的局面为 \(a\oplus b\oplus c = 0\) 时,无论他如何操作都无法使得操作后 \(a\oplus b\oplus c = 0\) 仍然成立。

证明:

对于操作一,操作前 \(a\oplus b = c\),假设操作后 \(a\oplus b = k\),那么 \(c = k\),然而题目要求最少取 \(1\) 颗石子,矛盾。

对于操作二,设 \(a, b, c\) 同时取走了 \(x\) 个,由于 \(a\oplus b = c\),因此 \(a\)\(b\)\(c\) 在二进制下的个位只有四种情况

  • 1 1 0
  • 0 0 0
  • 0 1 1
  • 1 0 1

则若 \(x\) 为奇数时,上述四种情况的末尾必然发生翻转,因此 \(a\oplus b\ne c\)

\(x\) 为偶数时,设 \(x\) 末尾有 \(k\)\(0\),就先不看后 \(k\) 位,如果从后往前数第一个 \(1\) 开始看,同 \(x\) 为奇数的情况。

因此重复这种操作,由于 \(a\oplus b\oplus c \ne 0, (a, b, c\leq 0)\),所以必不可能走到形如 0 0 0 的情况,因此先手立于不败之地,而后手不断面临着 \(a\oplus b\oplus c = 0\) 的情况,迟早有一天他会碰到 0 0 0 而输掉游戏。

因此若 \(a\oplus b\oplus c \ne 0\) 则先手必胜,否则先手必败。

代码实现

// Problem: Yet Another Number Game
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF282D
// Memory Limit: 250 MB
// Time Limit: 2000 ms
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-02-27 18:56:56

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;

const int N = 300 + 10;

int n;
namespace T2
{
    bool dp[N][N];
    
    void work()
    {
        dp[0][0] = 0;
        for(int i = 1; i <= N; i ++)
            dp[i][0] = dp[0][i] = 1;
        for(int i = 1; i <= N; i ++)
        {
            for(int j = 1; j <= N; j ++)
            {
                for(int k = 1; k <= i; k ++)
                    dp[i][j] |= !(dp[i - k][j]);
                for(int k = 1; k <= j; k ++)
                    dp[i][j] |= !(dp[i][j - k]);
                for(int k = 1; k <= min(i, j); k ++)
                    dp[i][j] |= !(dp[i - k][j - k]);
            }
        }
    }
} ;


int main()
{
    cin >> n;
    int a, b, c;
    if (n == 1)
    {
        cin >> a;
        if (a)
            cout << "BitLGM" << endl;
        else cout << "BitAryo" << endl;
    }
    else if(n == 2)
    {
        cin >> a >> b;
        T2::work();
        cout << (T2::dp[a][b] ? "BitLGM" : "BitAryo") << endl;
    }
    else
    {
        cin >> a >> b >> c;
        cout << ((a ^ b ^ c) ? "BitLGM" : "BitAryo") << endl;
    }

    return 0;
}

感谢 chicken_lizard 大佬的指导。

posted @ 2023-02-28 23:06  MoyouSayuki  阅读(33)  评论(0编辑  收藏  举报
:name :name