Luogu P2148 [SDOI2009]E&D

题目链接 \(Click\) \(Here\)

蒟蒻的人生第一道博弈论。真吉尔难啊。。。。

通常的博弈论写法似乎都是\(SG\)函数打表猜规律。本蒻其实本来想学一下博弈论的证明的,但后来发现果然还是打表快速又好用。

这个题中的模型可以拆分成\(n/2\)个游戏。对每个游戏的每堆石子单独打表,求其\(SG\)函数,可以得到结论:大小为\(N\)的石子堆,其拆分为两堆后对应的后继\(SG\)函数\(mex\)值为\(N-1\)。最终利用\(SG\)函数的性质,把每个游戏的\(SG\)函数合并,就是所求最初状态的\(SG\)函数。

打表代码:(感谢 @FlashHu 的题解

#include<cstdio>
#include<bitset>
#include<iostream>
using namespace std;

const int N = 10;
const int M = N + 1;//随便调大小

int ans[M][M];
bitset <M> s[M];

int mex (bitset <M> b) {
    int res = 0;
    while (b[res]) ++res;
    return res;
}

int main () {
    for(int i = 2; i <= N; ++i) {
        for (int j = 1, k = i - 1; k; ++j, --k) {
            s[i].set (ans[j][k] = mex (s[j] | s[k]));//枚举合并
        }
    }
    for (int i = 0; i < N; ++i) printf ("%3d", i); printf ("\n");
    for (int i = 1; i < N; ++i){//输出矩阵
        printf ("%2d:", i);
        for (int j = 1; i + j <= N; ++j) {
            printf ("%3d", ans[i][j]);
        }
        printf ("\n");
    }
    for(int i = 1; i <= N; ++i) {//输出对于每一个a,所有c+d=a的(c,d)的SG值集合
        printf ("%2d:SG%d ", i, mex (s[i]));
        cout << s[i] << endl;
    }
    return 0;
}

\(STD:\)

#include <bits/stdc++.h>
using namespace std;

int T, n, x, y, z, cnt, ans;

int main () {
    cin >> T;
    while (T--) {
        ans = 0;
        cin >> n; n >>= 1;
        while (n--) {
            cnt = 0;
            cin >> y >> z;
            x = (y - 1) | (z - 1);
            while (x & 1) {
                ++cnt;
                x >>= 1;
            }
            ans ^= cnt;
        }
        puts (ans ? "YES" : "NO");
    }
    return 0;
}

注意最终求的\(cnt\),其实也就是把两堆石子的\(SG\)合并,得到的单个游戏的\(SG\)函数值。

猜你喜欢

转载自www.cnblogs.com/maomao9173/p/10508468.html