题目:给定一个n*m的矩形,上面的坐标(x,y)满足0<=x<=n,0<=y<=m,每个坐标上面都有一枚硬币,有的是正面向上,有的是反面向上,每次操作可以选择一个矩形,这个矩形要满足x,y最大的那个顶点上的硬币是正面向上的,将4个顶点的硬币翻面。两个游戏者轮流操作,不能操作的人输,告诉你正面向上的硬币的坐标,问先手必胜还是先手必败。
思路:看论文,论文上面有详细介绍,要用到nim乘法运算
SG(x,y)=Nim_mult(x,y)
Nim_mult(x,y)=mex{Nim_mult(a,y)^Nim_mult(x,b)^Nim_mult(a,b)|0<=a<x,0<=b<y}。
代码:
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<list>
#include<numeric>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define INF 0x3f3f3f3fn
#define mm(a,b) memset(a,b,sizeof(a))
#define PP puts("*********************");
template<class T> T f_abs(T a){ return a > 0 ? a : -a; }
template<class T> T gcd(T a, T b){ return b ? gcd(b, a%b) : a; }
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
// 0x3f3f3f3f3f3f3f3f
//0x3f3f3f3f
int m[2][2]={0,0,0,1};
int Nim_Mult_Power(int x,int y){
if(x<2)
return m[x][y];
int a=0;
for(;;a++)
if(x>=(1<<(1<<a))&&x<(1<<(1<<(a+1))))
break;
int m=1<<(1<<a);
int p=x/m,s=y/m,t=y%m;
int d1=Nim_Mult_Power(p,s);
int d2=Nim_Mult_Power(p,t);
return (m*(d1^d2))^Nim_Mult_Power(m/2,d1);
}
int Nim_Mult(int x,int y){
if(x<y)
return Nim_Mult(y,x);
if(x<2)
return m[x][y];
int a=0;
for(;;a++)
if(x>=(1<<(1<<a))&&x<(1<<(1<<(a+1))))
break;
int m=1<<(1<<a);
int p=x/m,q=x%m,s=y/m,t=y%m;
int c1=Nim_Mult(p,s);
int c2=Nim_Mult(p,t)^Nim_Mult(q,s);
int c3=Nim_Mult(q,t);
return (m*(c1^c2))^c3^Nim_Mult_Power(m/2,c1);
}
int main(){
int T,n,x,y;
scanf("%d",&T);
while(T--){
scanf("%d",&n);//正面向上的硬币的个数
int sg=0;
for(int i=1;i<=n;i++){
scanf("%d%d",&x,&y);//读入正面向上的硬币的坐标
sg=(sg^Nim_Mult(x,y));
}
if(sg>0) printf("Have a try, lxhgww.\n");//先手必胜
else printf("Don't waste your time.\n");//先手必败
}
return 0;
}