卧薪尝胆,厚积薄发。
高手过招
Date: Sat Oct 27 18:57:30 CST 2018
In Category:
NoCategory
Description:
在一个
$n\times 20$
的棋盘上,对于一个棋子,能将它向右跳到第一个空格,如果右边没有空格,则不能移动这个棋子,如果所有棋子都不能移动,那么将输掉这场比赛,问先手能否胜利。
$1\leqslant testcases\leqslant 100,1\leqslant n\leqslant 1000$
Solution:
每行之间是独立的,而一行最长只有
$20$
,可以想到预处理每行答案。
感觉更加理解
$SG$
函数了,如果只有一个游戏,那么可以只开一个
$bool$
型变量表示这个局面是否能赢,如果后继状态都为赢,那这个状态就输,否则就赢,但是这样不能把游戏组合起来,所以就有了
$SG$
函数,各个子游戏的
$SG$
函数异或和就是总游戏的
$SG$
值。
这个题目的话,先预处理出每个状况的
$SG$
值,具体来说就是维护一个下一个空位,倒序枚举每个棋子往后跳并用
$mex$
求
$SG$
,然后只要把每一行的值异或起来判断是否为
$0$
即可。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
#define MAXL 22
int SG[1 << MAXL];
bool vis[MAXL];
void calc_SG(int x)
{
memset(vis,false,sizeof(vis));
int nxt = 0;
for(int i = 1;i <= 20;++i)
{
if(x & (1 << (i - 1)))
{
if(nxt != 0)
{
vis[SG[x ^ (1 << (i - 1)) ^ (1 << (nxt - 1))]] = true;
}
}
else
{
nxt = i;
}
}
int k = 0;
for(;vis[k];++k);
SG[x] = k;
return;
}
int main()
{
for(int i = 1;i < (1 << 20);++i)calc_SG(i);
int testcases;
scanf("%d",&testcases);
while(testcases--)
{
int res = 0;
int n;
scanf("%d",&n);
for(int i = 1;i <= n;++i)
{
int m;
scanf("%d",&m);
int p;
int tmp = 0;
for(int k = 1;k <= m;++k)
{
scanf("%d",&p);p = 20 - p + 1;
tmp |= (1 << (p - 1));
}
res ^= SG[tmp];
}
if(res != 0)puts("YES");
else puts("NO");
}
return 0;
}
In tag:
数学-SG函数
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡