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 的区域。
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
= =
- -
- -
- -
- -
HINT
对于40% 的数据,满足1 ≤ n;m ≤ 5。
对于100% 的数据,满足1 ≤ n;m ≤ 100,1 ≤ T ≤ 50。
正解:$SG$函数。
又是这种硬币问题。。我开始以为每次翻转一个矩形,然后写了个$dp$求$SG$函数并成功爆零。。
然后写一个搜索可以发现,如果$i=1$或$j=1$,$SG[i][j]=lb(max(i,j))$,否则$SG[i][j]=2^{i+j-2}$。
但是太大了,所以我们只记录二进制的每一位是否为$0$就行了。
1 #include <bits/stdc++.h> 2 #define il inline 3 #define RG register 4 #define ll long long 5 #define lb(x) (x & -x) 6 7 using namespace std; 8 9 int cnt[205],bin[205],n,m,ans; 10 11 il int gi(){ 12 RG int x=0,q=1; RG char ch=getchar(); 13 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 14 if (ch=='-') q=-1,ch=getchar(); 15 while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); 16 return q*x; 17 } 18 19 il char gc(){ 20 RG char ch=getchar(); 21 while (ch!='H' && ch!='T') ch=getchar(); return ch; 22 } 23 24 il void get(RG int x,RG int y){ 25 if (x==1){ cnt[bin[lb(y)]]^=1; return; } 26 if (y==1){ cnt[bin[lb(x)]]^=1; return; } 27 cnt[x+y-2]^=1; return; 28 } 29 30 il void work(){ 31 n=gi(),m=gi(),memset(cnt,0,sizeof(cnt)); 32 for (RG int i=1;i<=n;++i) 33 for (RG int j=1;j<=m;++j) 34 if (gc()=='T') get(i,j); 35 for (RG int i=0;i<=200;++i) 36 if (cnt[i]){ puts("-_-"); return; } 37 puts("=_="); return; 38 } 39 40 int main(){ 41 #ifndef ONLINE_JUDGE 42 freopen("color.in","r",stdin); 43 freopen("color.out","w",stdout); 44 #endif 45 for (RG int i=2;i<=100;++i) bin[i]=bin[i>>1]+1; 46 RG int T=gi(); while (T--) work(); return 0; 47 }