[ZJOI2009]染色游戏

https://www.zybuluo.com/ysner/note/1232377

题面

一个\(n*m\)的长方形上,每个格子上有一硬币。每次需选一联通块并全部翻面,前提是其右下角硬币必须初始为反面。双方轮流行动,问先手是否有必胜策略。

  • \(n,m\leq50\)

解析

这是一个二维翻硬币问题。

先从一维说起。
一维翻硬币问题有个结论:局面的SG值等于局面中所有反面朝上的硬币单独存在时的SG值的异或和

于是试着算一下:(其实我就是想试试怎么算\(SG\)值)
\(x\)表示第\(x\)个硬币单独反面向上的情况。
据(\(mex\)表示取括号内不包含的 最小非负整数)$$SG(x)=mex_{0\leq i<x}(\bigotimes_{j=1}^iSG(j))$$
\(SG(0)=0\)

\(SG(1)=1\),因操作后状态\((1,0,0,0)\)可转移到状态\((0,0,0,0)\);

\(SG(2)=2\),因操作后状态\((0,1,0,0)\)可转移到状态\((0,0,0,0)\)和状态\((1,0,0,0)\)

\(mex(0,SG(1))=1\);

\(SG(3)=1\),因操作后状态\((0,0,1,0)\)可转移到状态\((0,0,0,0)\)、状态\((0,1,0,0)\)和状态\((1,1,0,0)\)这个状态可由\(x=1\)状态异或\(x=2\)状态产生),

\(mex(0,SG(1),SG(1)\bigotimes SG(2))=2\);

\(SG(4)=4\),因操作后状态\((0,0,0,1)\)可转移到状态\((0,0,0,0)\)、状态\((0,0,1,0)\)和状态\((0,1,1,0)\)\((1,1,1,0)\)

\(mex(0,SG(1),SG(1)\bigotimes SG(2),SG(1)\bigotimes SG(2)\bigotimes SG(3))=4\);

同理,\(SG(5)=4\)
发现规律,\(SG(x)=lowbit(x)\)(相当于只保留最高位)。

同理,可以试着算二维状态的\(SG\)值,方法与上类似,懒得打过程了。(矩阵转动后\(SG\)值不变

于是得到规律:
\(i==1||j==1\)时,\(SG(i,j)=lowbit(i+j-1);\)
除此之外,\(SG(i,j)=2^{i+j-2}\)

\(long\ long\)随便用\(bool\)存下各位是否为\(1\)就好了。

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ll long long
#define re register
#define il inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int N=205,mod=2017;
int n,m,mp[2000],ans;
bool vis[N];
il ll gi()
{
  re ll x=0,t=1;
  re char ch=getchar();
  while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  if(ch=='-') t=-1,ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  return x*t;
}
il int SG(re int x,re int y)
{
  if(x==1||y==1) return mp[(x+y-1)&(-x-y+1)];
  else return x+y-2;
}
int main()
{
  ios::sync_with_stdio(false);
  re int T;cin>>T;
  fp(i,1,10) mp[1<<i]=i;
  while(T--)
    {
      memset(vis,0,sizeof(vis));
      cin>>n>>m;
      fp(i,1,n) fp(j,1,m)
    {
      re char c;cin>>c;
      if(c=='T') vis[SG(i,j)]^=1;
    }
      re int tag=0;
      fq(i,n+m-1,0) if(vis[i]) {tag=1;break;}
      puts(tag?"-_-":"=_=");
    }
  
  return 0;
}
posted @ 2018-07-30 21:10  小蒟蒻ysn  阅读(409)  评论(0编辑  收藏  举报