链接
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1502
题解
一开始我也是从组合游戏的和的角度取考虑的,但是我把
X
X
X作为一个游戏的左右边界了,这样思路就进了死胡同
大方向是这样的,枚举先手放在哪里,然后对这种新局面判断是不是先手必败
- 首先判断是不是已经出现了三个 X X X相连的情形,如果出现了,那么当前局面就属于先手必败
- 另外XX.,X.X,这两种情形一旦出现,那就是先手必胜了
排除以上两种情形之后,剩下的
X
X
X的两两间隔至少为
2
2
2
聪明的玩家不会往一个
X
X
X的左边或者右边的相邻或相隔
1
1
1的位置放一个
X
X
X,他如果这么放,对手下一步就赢了
那么我就把这一块区域看作是禁区,谁放谁输
最后不得不放在禁区的人就输了
现在问题变了,每次选一个非禁区的点,放下一个
X
X
X,把以它为中心的
5
5
5个格子都设为禁区,最后不能操作的人输
不难发现,我把禁区标注出来之后,剩下的区域每个区域都是一个单独的组合游戏,整个游戏变成组合游戏的和
所以
S
G
SG
SG定理可以使用了
初始状态,
s
g
(
1
)
=
s
g
(
2
)
=
s
g
(
3
)
=
1
,
s
g
(
4
)
=
s
g
(
5
)
=
2
sg(1)=sg(2)=sg(3)=1,sg(4)=sg(5)=2
sg(1)=sg(2)=sg(3)=1,sg(4)=sg(5)=2
当
i
>
5
i>5
i>5时,
s
g
(
i
)
=
m
e
x
{
s
g
(
i
−
3
)
,
s
g
(
i
−
4
)
,
s
g
(
i
−
5
)
,
s
g
(
j
)
x
o
r
s
g
(
i
−
5
−
j
)
}
sg(i)=mex\{sg(i-3),sg(i-4),sg(i-5),sg(j)\ xor\ sg(i-5-j)\}
sg(i)=mex{sg(i−3),sg(i−4),sg(i−5),sg(j) xor sg(i−5−j)},其中
i
−
5
−
j
>
0
i-5-j>0
i−5−j>0
这其实就是枚举在哪个位置放一个
X
X
X,把序列分成两部分,每一部分又是一个子游戏,利用
S
G
SG
SG定理来计算整个游戏的
S
G
SG
SG函数
当前游戏的
S
G
SG
SG函数,就是每个连续非禁区的
s
g
sg
sg的
n
i
m
nim
nim和
代码
//组合游戏
#include <bits/stdc++.h>
#define cl(x) memset(x,0,sizeof(x))
#define maxn 210
int N, sg[maxn], vis[maxn], a[maxn], lis[maxn];
char now[maxn];
void preprocess()
{
int i, j;
sg[1]=sg[2]=sg[3]=1;
sg[4]=sg[5]=2;
for(i=6;i<maxn;i++)
{
cl(vis);
vis[sg[i-3]]=vis[sg[i-4]]=vis[sg[i-5]]=1;
for(j=1;j<=i-5-j;j++)vis[sg[j]^sg[i-5-j]]=1;
for(j=0;vis[j];j++);
sg[i]=j;
}
}
bool check()
{
int i, j, c, nim_sum=0;
cl(a);
for(i=2;i<N;i++)if(now[i]==now[i-1] and now[i]==now[i+1] and now[i]=='X')return true;
for(i=2;i<N;i++)
{
c=(now[i-1]=='X')+(now[i]=='X')+(now[i+1]=='X');
if(c==2)return false;
}
for(i=1;i<=N;i++)
if(now[i]=='X')
for(j=0;j<3;j++)
{
if(i+j<=N)a[i+j]=1;
if(i-j>0)a[i-j]=1;
}
for(i=1;i<=N;i=j+1)
if(a[i]){j=i;continue;}
else
{
for(;a[j+1]==0 and j<N;j++);
nim_sum^=sg[j-i+1];
}
return nim_sum==0;
}
int main()
{
int i, j, T;
preprocess();
scanf("%d",&T);
while(T--)
{
scanf("%s",now+1); N=strlen(now+1);
*lis=0;
for(i=1;i<=N;i++)
{
if(now[i]=='.')
{
now[i]='X';
if(check())lis[++*lis]=i;
now[i]='.';
}
}
if(*lis)printf("WINNING\n%d",lis[1]);
for(i=2;i<=*lis;i++)printf(" %d",lis[i]);
if(!*lis)printf("LOSING\n");
putchar(10);
}
return 0;
}