Description
一共n × m 个硬币,摆成n × m 的长方形。dongdong 和xixi 玩一个游戏,每次可以选择一个连通块,并把其中的硬币全部翻转,但是需要满足存在一个硬币属于这个连通块并且所有其他硬币都在它的左上方(可以正左方也可以正上方),并且这个硬币是从反面向上翻成正面向上。dongdong 和xixi 轮流操作。如果某一方无法操作,那么他(她) 就输了。dongdong 先进行第一步操作,假设双方都采用最优策略。问dongdong 是否有必胜策略。
Input
第一行一个数T,表示他们一共玩T 局游戏。
接下来是T 组游戏描述。每组游戏第一行两个数n;m,接下来n 行每行m 个字符,第i 行第j 个字符如果是“H” 表示第i 行第j 列的硬币是正面向上,否则是反面向上。第i 行j 列的左上方是指行不超过i 并且列不超过j 的区域。
Output
对于每局游戏,输出一行。如果dongdong 存在必胜策略则输出“-_-”(不含引号)否则输出“=_=”(不含引号)。(注意输出的都是半角符号,即三个符号ASCII 码分别为45,61,95)
Sample Input
3
2 3
HHH
HHH
2 3
HHH
TTH
2 1
T
H
Sample Output
=_=
-_-
-_-
被这道题虐了一下午……首先考虑一维的情况:
·N 枚硬币排成一排,有的正面朝上,有的反面朝上。我们从左开始对硬币按1到N编号。
·游戏者根据某些约束翻硬币(如:每次只能翻连续的几枚),但他所翻动的硬币中,最右边的必须是从反面面翻到正面。
·谁不能翻谁输。
由SG函数的一个性质,则有:
局面的SG值为局面中每个反面朝上的硬币单一存在时的SG值的异或和。
设SG(n)为第n个硬币为反面向上,其余硬币都为正面向上这个局面的SG值。
则有:
其中,mex(minimal excludant)是定义在自然数集合上的操作。它的自变量是任意自然数集合,函数值是不属于该集合的最小自然数。
证明从略。举几个特殊的例子予以说明:
显然SG(1) = 1;SG(2) = 2;SG(3) = mex{0, 2, 2 XOR 1} = 1;
SG(4) = mex{0, 1, 1 XOR 2, 1 XOR 2 XOR 1} = 4;
SG(5) = mex(0, 4, 4 XOR 1, 4 XOR 1 XOR 2, 4 XOR 1 XOR 2 XOR 1) = 1;
……
可以简单地得出SG(n) = lowbit(n)。
再来看二维的情况。
上面所说的分解仍然有效,即可以把若干个反面朝上的硬币隔离起来单独分析,最后再求一个异或即可。
先看两个推理:
记左上角的点为(0, 0),则有:
最后求所有SG值的异或和时,由于结果比较大(最大能达到2^100),所以只记录这个二进制数中每一位的情况,最后判断这个二进制数中是否有至少有一位为1,若是,则有必胜策略,否则无必胜策略。
再展览一下我的草稿纸………
Accode:
#include <cstdio>
#include <cstdlib>
#include <algorithm>
bool tag[210];
int T, n, m;
inline int sg(int i, int j)
{
if (i && j) return i + j;
int cnt = 0;
for (int tmp = i + j + 1; !(tmp & 1); tmp >>= 1)
++cnt;
return cnt;
}
int main()
{
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
for (scanf("%d", &T); T; --T)
{
scanf("%d%d", &n, &m);
memset(tag, 0, sizeof tag);
for (int i = 0; i < n; ++i)
{
scanf("\n");
for (int j = 0; j < m; ++j)
if (getchar() == 'T')
tag[sg(i, j)] ^= 1;
}
bool ok = 0;
for (int i = 0; i < n + m - 1; ++i)
if (tag[i])
{printf("-_-\n"); ok = 1; break;}
if (!ok) printf("=_=\n");
}
return 0;
}