1457: 棋盘游戏
Time Limit: 5 Sec Memory Limit: 64 MB
Submit: 343 Solved: 195
[Submit][Status][Discuss]
Description

有一个100 * 100的棋盘,其中左下角的编号为(0, 0), 右上角编号为(99, 99)。棋盘上有N个Queen,最开始第i个Queen的位置为(Xi, Yi)。现在有两个玩家依次来操作,每一次一个玩家可以选择其中一个Queen,将它跳到(Xi – k, Yi)或(Xi, Yi - k)或(Xi – k, Yi - k), 其中k > 0。注意在游戏的过程中,一个格子里面可能出现多个Queen。如果谁先将任意一个Queen移动到(0, 0), 谁就获胜。问先手必胜还是后手必胜?

Input

注意本题是多组数据。 第一行有一个数T, 表示数据组数。 接下来有T组数据,每组数据的第一行一个正整数N表示Queen的个数。接下来N行每行两个数表示第i个Queen的初始位置Xi, Yi(0 <= Xi <= 99, 0 <= Yi <= 99)。

Output

对于每一组数据,你需要输出是先手必胜还是后手必胜。 如果是先手必胜,输出“^o^“, 如果是后手必胜,输出”T_T”。

Sample Input

2

2

3 4

3 5

3

3 2

4 2

3 1

Sample Output

^o^

T_T

数据范围

T <= 10, N <= 1000

HINT

Source


【分析】
我们熟悉的SG函数一般是基于“无法操作者判负”的条件。
那么我们把题目转化一下,标记出来一步就能到达(0,0)点的”禁区”,如果某一个点无论怎么走都会走到禁区,那么它就相当于Nim游戏中的无法操作。它的sg函数为0,然后递推出来100*100棋盘的sg函数。
注意禁区点 没有 sg函数,并不是说禁区的sg为0。

最后异或一发就好啦。


【代码】

//bzoj 1457
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
int n,m,x,y,T,cnt;
int sg[105][105],vis[10005];
inline void init()
{
int i,j,k;
fo(i,0,100) if(i)
fo(j,0,100) if(j && i!=j)
{
if(i==2 && j==1) continue;
if(i==1 && j==2) continue;
fo(k,0,i-1) vis[sg[k][j]]=cnt;
fo(k,0,j-1) vis[sg[i][k]]=cnt;
fo(k,1,min(i,j)) vis[sg[i-k][j-k]]=cnt;
for(k=0;;k++) if(vis[k]!=cnt)
{
sg[i][j]=k;break;
}
}
}
int main()
{
init();
int i,j,x,y;
scanf("%d",&T);
while(T--)
{
int ans=0,flag=0;
scanf("%d",&n);
fo(i,1,n)
{
scanf("%d%d",&x,&y);
if(!x || !y || x==y)
flag=1;
ans^=sg[x][y];
}
if(ans || flag) puts("^o^");
else puts("T_T");
}
return 0;
}