一、题目
二、解法
这显然是一个不公平游戏,我们把相邻两个X
之间的所有.
(长度为
l
e
n
len
len)拿出来讨论。
- 种类 1 1 1: l e n < b len<b len<b,这种线段没人能选,可以忽略。
- 种类 2 2 2: b ≤ l e n < a b\leq len<a b≤len<a,这种线段只有后手能选,只要存在这种线段,那么后手必胜,因为这个东西就像一个复活甲(手动滑稽),后手到不得已之时就可以保命。
- 种类 3 3 3: a ≤ l e n < 2 b a\leq len<2b a≤len<2b,这种线段两者都选,我们最后需要通过此种线段数量的奇偶性来判断胜负态。
- 种类 4 4 4: l e n ≥ 2 b len\geq 2b len≥2b,这种线段如果有两次及以上,那么后手必胜,因为后手一定可以留一块 [ b , a ) [b,a) [b,a)的线段,就有了复活甲。如果出现了一次,先手就要利用自己先操作的优势破坏它。
算法的基本框架就显现了出来,我们先看有没有种类 2 2 2,然后看种类 4 4 4的数量,如果为 2 2 2先手败,如果为 0 0 0判断种类 3 3 3的奇偶性。否则枚举先手对种类 4 4 4的操作,左边剩下和右边剩下的都不能是种类 2 2 2和 4 4 4,然后判断一下种类 3 3 3的奇偶性即可。
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int M = 300005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int T,n,a,b;char s[M];
vector<int> g;
int work()
{
int cnt=0,t=0,tmp=0;
for(int i=0;i<g.size();i++)
{
int x=g[i];
if(x>=b && x<a) return 0;
if(x>=a && x<2*b) cnt++;
if(x>=2*b) t++,tmp=x;
}
if(t==0) return cnt&1;
if(t>1) return 0;
for(int i=1;i<=tmp-a+1;i++)
{
int l=i-1,r=tmp-i-a+1;
if(l>=2*b || r>=2*b || (l<a && l>=b) || (r<a && r>=b))
continue;
int d=(l>=a && l<2*b)+(r>=a && r<2*b);
if((cnt+d)%2==0)
return 1;
}
return 0;
}
int main()
{
T=read();
while(T--)
{
a=read();b=read();
scanf("%s",s),n=strlen(s);
g.clear();
for(int i=0;i<n;i++)
{
if(s[i]=='X') continue;
int j=i;
while(s[j]=='.' && j<n) j++;
g.push_back(j-i);
i=j-1;
}
puts(work()?"YES":"NO");
}
}