洛谷2575 高手过招

原题链接

显然每一行都是一个独立的\(ICG\),所以针对每一行进行计算\(SG\)函数,最后异或合并即可。
直接预处理出所有状态的\(SG\)函数,输入后直接调用即可。
简略讲下预处理。
将每一行都看成是二进制数,棋子是\(1\),空位是\(0\)
二进制枚举所有状态,然后用位运算模拟棋子向右走,因为枚举是从小到大的,而棋子只会右移,所以右移后的二进制数一定被处理过了,而这也是该状态能够到达的状态,直接调用到达状态的\(SG\)函数标记,最后取\(mex\)操作即可。

#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1 << 21;
int SG[N];
bool v[500];
inline int re()
{
    int x = 0;
    char c = getchar();
    bool p = 0;
    for (; c < '0' || c > '9'; c = getchar())
        p |= c == '-';
    for (; c >= '0' && c <= '9'; c = getchar())
        x = x * 10 + c - '0';
    return p ? -x : x;
}
int main()
{
    int i, j, n, m, x, la, o = N >> 1, t, s;
    for (i = 1; i <= o; i++)
    {
        la = -1;
        memset(v, 0, sizeof(v));
        for (j = 0; j < 20 && i >= (1 << j); j++)
        {
            if (i & (1 << j))
            {
                if (~la)
                {
                    x = i ^ (1 << j);
                    x |= 1 << la;
                    v[SG[x]] = 1;
                }
            }
            else
                la = j;
        }
        for (j = 0; v[j]; j++);
        SG[i] = j;
    }
    t = re();
    while (t--)
    {
        n = re();
        for (s = 0, i = 1; i <= n; i++)
        {
            m = re();
            for (x = 0, j = 1; j <= m; j++)
                x |= 1 << (20 - re());
            s ^= SG[x];
        }
        s ? printf("YES\n") : printf("NO\n");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Iowa-Battleship/p/9858126.html