码迷,mamicode.com
首页 > 其他好文 > 详细

POJ 2315(Nim k 博弈)

时间:2020-10-05 21:47:14      阅读:26      评论:0      收藏:0      [点我收藏+]

标签:个性   lib   异或   lse   queue   lex   const   bitset   lang   

题意

问题转化成, 有 \(N\) 堆石子, 博弈双方每次可以选择不超过 \(M\) 堆, 每堆取不超过 \(X\) 个, 总的取石子数至少为一. 问胜利方.

题解

对于单堆来说是一个巴什博弈, \(SG\) 值为石子数模 \(X+1\).

我们回忆 \(M=1\) 时的 \(Nim\) 博弈, 它利用了这样的性质:

  • 设石子数为 \(a[i]\), 对于 \(x<a[i]\), 有 \(x\)^\(a[i]>0\).

  • 设所有石子数的异或值为 \(S\), \(S=0\) 时, 后继一定是 \(S\not=0\)

  • \(S\not=0\) 时后继一定存在 \(S=0\).

关于第二点的证明, 是利用了改变单堆石子数时, 一定是对 \(S\) 异或一个正数. 而第三点是可以证明存在一个数, 改变它就对 \(S\) 异或了 \(S\).

考虑现在面对的 \(M>1\) 情况, 我们为了利用第一个性质仍然把石子数转成二进制. "异或"时每一位做模 \(M+1\) 的加法(后面提到的异或都是这样). 当 \(S=0\) 时, 取 \(1-M\) 堆石子就相当于对 \(S\) 进行了对应次的"异或", 结果必不为 \(0\). \(S\not=0\) 时能找到方案使得 \(S=0\), 其实就是把 \(S\) 分解成若干个合法的二进制数, 取相应数量的石子堆来实现.

代码

#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
using namespace std;
#define ll long long
#define inc(i, l, r) for (int i = l; i <= r; i++)

const double PI = acos(-1.0);

int N, M, L, R;
int sg[35], num[35];

int main() {
    while (scanf("%d %d %d %d", &N, &M, &L, &R) != EOF) {
        inc(i, 0, 31) num[i] = 0;
        int mx = L / (2 * PI * R);
        int x;
        inc(i, 1, N) {
            scanf("%d", &x);
            sg[i] = (int)((ceil)(x / (2 * PI * R))) % (mx + 1);
            int top = 0;
            while (sg[i]) {
                num[top++] += sg[i] & 1;
                sg[i] >>= 1;
            }
        }
        int fail = 1;
        inc(i, 0, 31) if (num[i] % (M + 1)) fail = 0;
        if (fail)
            printf("Bob\n");
        else
            printf("Alice\n");
    }
}

POJ 2315(Nim k 博弈)

标签:个性   lib   异或   lse   queue   lex   const   bitset   lang   

原文地址:https://www.cnblogs.com/hs-zlq/p/13762517.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!