BZOJ1434:[ZJOI2009]染色游戏(博弈论)
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的区域。
1≤n;m≤100,1≤T≤50。
Output
对于每局游戏,输出一行。
如果dongdong 存在必胜策略则输出“- -”(不含 引号) 否则输出“= =”(不含引号)。
(注意输出的都是半角符号,即三个符号 ASCII 码分别为45,61,95)
Sample Input
32
3
HHH
HHH
2 3
HHH
TTH
2 1
T
H
3
HHH
HHH
2 3
HHH
TTH
2 1
T
H
Sample Output
= =
- -
- -
- -
- -
Solution
有一个叫$yyb$的神仙她说这个题打表就可以了,于是我就抄了个爽。
首先要知道翻硬币游戏的一个结论。
假设你操作的最右(下)方的硬币必须是正着的,那么局面的$SG$值为局面中每个正面朝上的棋子单一存在时的$SG$值的异或和。
单一存在时$SG$的异或和就可以打表搞了。规律是:(左上角为$(0,0)$)
如果$i=0$且$j=0$,$SG(i,j)=2^{i+j}$。
否则$SG(i,j)=lowbit(i+j+1)$。
可以发现虽然这个$SG$值太大,可他二进制下都只有一位啊,开个$vis$数组记一下就好了。
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #define N (509) 6 using namespace std; 7 8 int T,n,m,flag,vis[N]; 9 char s[N]; 10 11 int lowbit(int x) {return x&(-x);} 12 13 int SG(int i,int j) 14 { 15 if (i && j) return i+j; 16 return log2(lowbit(i+j+1)); 17 } 18 19 int main() 20 { 21 scanf("%d",&T); 22 while (T--) 23 { 24 flag=0; 25 memset(vis,0,sizeof(vis)); 26 scanf("%d%d",&n,&m); 27 for (int i=0; i<n; ++i) 28 { 29 scanf("%s",s); 30 for (int j=0; j<m; ++j) 31 if (s[j]=='T') vis[SG(i,j)]^=1; 32 } 33 for (int i=0; i<n+m-1; ++i) 34 if (vis[i]) flag=1; 35 if (flag) puts("-_-"); 36 else puts("=_="); 37 } 38 }