1 条题解

  • 1
    @ 2021-7-26 9:00:11

    题目分析

    发现题目很像 Nim 游戏,有区别在于每次取石子只能取某些给定的数值。但看数据范围发现 m,aim, a_i 很小,可以直接递推求 SG 函数。
    然后根据 SG 定理 求出子游戏的 Nim 和(记为 kk)。

    1. k=0k = 0 :先手必败
    2. k>0k > 0 :先手必胜。至于求第一步的方案,可以直接枚举取哪一堆、取多少石子,判断是否能使 k0k \rightarrow 0 就行了。

    代码

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #include <bitset>
    
    const int N = 10;
    const int SIZ = 1e3;
    
    int A[N + 5], n;
    int B[N + 5], m;
    
    int SG[SIZ + 5];
    
    void GetSG();
    
    int main() {
      std::ios::sync_with_stdio (0), std::cin.tie (0);
    
      std::cin >> n;
      for (int i = 1; i <= n; ++i) std::cin >> A[i];
      std::cin >> m;
      for (int i = 1; i <= m; ++i) std::cin >> B[i];
    
      GetSG();
      int sxor = 0;
      for (int i = 1; i <= n; ++i) sxor ^= SG[A[i]];
      if (!sxor) std::cout << "NO";
      else {
        std::cout << "YES" << "\n";
        int k = (int)log2 (sxor);
        bool flag = false;
        for (int i = 1; i <= n; ++i) {
          for (int j = 1; j <= m && B[j] <= A[i]; ++j)
            if ((SG[A[i] - B[j]] ^ SG[A[i]] ^ sxor) == 0) {
              std::cout << i << " " << B[j], flag = true; break;
            }
          if (flag) break;
        }
      }std::cout << "\n";
      return 0;
    
    }void GetSG() {
      std::bitset <SIZ + 1> S;
      for (int i = 1; i <= SIZ; ++i) {
        S.reset();
        for (int j = 1; j <= m && B[j] <= i; ++j) S.set (SG[i - B[j]]);
        S.flip(), SG[i] = S._Find_first();
      }
    }
    
    • 1

    信息

    ID
    1874
    时间
    1000ms
    内存
    256MiB
    难度
    7
    标签
    (无)
    递交数
    19
    已通过
    9
    上传者