Problem
Hint
N,M≤20
Solution
- 前置技能:SG定理。
- 首先,这个游戏其实可以分成两个子游戏:对于i+j&1=0的位置是一个;对于i+j&1=1的位置是一个。
- 譬如,对于一个n*m的矩阵,我们如下划分:
- 如图,红色为一个子游戏,蓝色为另一个子游戏。
- 它的正确性就在于:我们肯定不会因为操作了红色格子而影响到蓝色格子;也不会因为操作了蓝色格子而影响到红色格子。
- 菱形图不太好处理,考虑将整个图转化为正方形。
- 我们将整个图沿矩阵中心顺时针(或逆时针)旋转45°,将红色、蓝色分别抽出来,得到下图:
- 拿红色区域来说,新图的(1,2)、(2,1)、(2,2)、(2,3)分别对应原图的(1,1)、(3,1)、(2,2)、(1,3)。旋转方法具体看Code。
- 这样,原图中的’L’影响的区域应为’\’形,新图则为’|’形;原图中的’R’影响的区域应为’/’形,新图则为’——’形。
- 考虑使用四个数x1、y1、x2、y2表示一个状态,代表新图中的一个左上角、右下角。这样即可表示新图中的一个矩形。
- 我们现在要求出SG[1][1][n+m>>1][n+m>>1]。(新图中的矩形[1][1][n+m>>1][n+m>>1]的SG值)
- 可以暴枚一个点(i,j),我们要操作它。
- 若点(i,j)的值为’L’,则影响区域为’|’形,即一条x=i的直线。这样可以将当前的状态now分为左右两个子矩形a、b。
- 若点(i,j)的值为其他,也差不多。
- 现在,关键是要通过a、b求解SG(now)。
- 易知状态{a,b}为状态now的一个后继状态。换句话说,先手可以通过一步操作,将状态now变成状态{a,b}。
- 先考虑一下SG({a,b})的取值。
- SG({a,b})应为SG(a)^SG(b)。
- 证明的话,因为a、b是两个互不影响、互不相交的子游戏,所以可以直接套用SG定理。
- 设to(i)表示状态i的后继状态的集合。那么SG(i)=mex{SG(to(i))}。
- 因为使用mex的话,先手就可以通过一步操作将SG值变成一个更小的数。这样就类比普通的取石子游戏。
- 那么,总共有 n2∗m2 n 2 ∗ m 2 种状态。可以考虑记忆化搜索。
- 最后,我们求出SG(红色区域)和SG(蓝色区域后),答案即为两者的异或和。
- 因为红色区域和蓝色区域也是两个互不影响、互不相交的子游戏,所以依然可以直接套用SG定理。
- 时间复杂度: O(n3∗m3) O ( n 3 ∗ m 3 ) 。
Code
#include <bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define mem(a,x) memset(a,x,sizeof a)
#define clear(a) mem(a,0)
#define init mem(SG,200)
const int N=21;
int i,j,n,m,a[N][N],b[N][N],SG1,SG2,SG[N][N][N][N];
char c[N][N];
int calc(int a[N][N],int x1,int y1,int x2,int y2)
{
if( x1>n || y1>n || !x2 || !y2 ) return 0;
if(SG[x1][y1][x2][y2]>=0) return SG[x1][y1][x2][y2];
int i,j,to;
bool bz[N<<1]; clear(bz);
fo(i,x1,x2)
fo(j,y1,y2)
if(a[i][j])
{
switch(a[i][j])
{
case 76: to=calc(a,x1,y1,x2,j-1)^calc(a,x1,j+1,x2,y2); break;
case 82: to=calc(a,x1,y1,i-1,y2)^calc(a,i+1,y1,x2,y2); break;
case 88: to=calc(a,x1,y1,i-1,j-1)^calc(a,x1,j+1,i-1,y2)^calc(a,i+1,y1,x2,j-1)^calc(a,i+1,j+1,x2,y2); break;
}
bz[to]=1;
}
fo(i,0,n+m) if(!bz[i]) return SG[x1][y1][x2][y2]=i;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
clear(a); clear(b);
fo(i,1,n) scanf("%s",c[i]+1);
fo(i,1,n)
fo(j,1,m)
if(i+j&1)
b[i+j>>1][n+1-i+j>>1]=c[i][j];
else a[i+j>>1][n+1-i+j>>1]=c[i][j];
init;
SG1=calc(a,1,1,n+m>>1,n+m>>1);
init;
SG2=calc(b,1,1,n+m>>1,n+m>>1);
printf( SG1^SG2 ? "WIN\n" : "LOSE\n" );
}
}