题目
思路
我不得不发出这样的感慨:游戏实在是太不公平了!
显然每个 .
极长段可以单独考虑。但是,最神奇的事莫过于:只要存在
b
⩽
l
e
n
<
a
b\leqslant len<a
b⩽len<a 的段,则
B
o
b
\sf Bob
Bob 必胜,无论先后手。策略也既简单:只要存在
l
e
n
<
a
len<a
len<a,就操作这样的段。否则
A
l
i
c
e
\sf Alice
Alice 已经无法操作了;此时
B
o
b
\sf Bob
Bob 还留有那个
b
⩽
l
e
n
<
a
b\leqslant len<a
b⩽len<a 可以操作,胜。
同理,当存在 l e n ⩾ 2 b len\geqslant 2b len⩾2b 的段时, B o b \sf Bob Bob 先手必胜:他只需要造出一个长度为 b b b 的段就行了。
如果二者都不存在呢?那么只剩 l e n < b len<b len<b,可忽略,和 a ⩽ l e n < 2 b a\leqslant len<2b a⩽len<2b,每个人都只能操作一次,直接数奇偶性。
于是 B o b \sf Bob Bob 先手必败的唯一情形是:仅有 l e n < b len<b len<b 和偶数个 a ⩽ l e n < 2 b a\leqslant len<2b a⩽len<2b 。
所以我们可以想想 A l i c e \sf Alice Alice 该干什么。如果存在 b ⩽ l e n < a b\leqslant len<a b⩽len<a 或者 l e n ⩾ 2 b len\geqslant 2b len⩾2b,她就必须要去处理;如果有多个,直接完蛋。仅有一个,则可以枚举她的操作是什么,然后看看 B o b \sf Bob Bob 能不能赢。如果一个都没有,那就已经进入 “每个人只能操作一次” 的境地了,直接数数量。
时间复杂度 O ( ∣ S ∣ ) \mathcal O(|S|) O(∣S∣) 。总结一下,有趣的点在于: B o b \sf Bob Bob 的必胜判断,竟然不需要通盘考虑;只要存在一个段就能直接取胜,恐怖如斯!没有极强的观察能力,就难搞了……
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar())
if(c == '-') f = -f;
for(; isdigit(c); c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MAXN = 300010;
char str[MAXN]; int a, b;
int cnt[4], x[MAXN];
int getRank(int len){
if(len < b) return 2; // don't care
if(len < a || (b<<1) <= len) return 0; // emergency
return 1; // useful, but not that important
}
int main(){
for(int T=readint(); T; --T){
a = readint(), b = readint();
scanf("%s",str+1);
int n = strlen(str+1), tot = 0;
str[n+1] = '#'; // whatever
for(int lst=1,i=1; i<=n+1; ++i)
if(str[i] != '.'){
if(lst != i) x[++ tot] = i-lst;
lst = i+1; // till next
}
cnt[0] = cnt[1] = cnt[2] = 0;
rep(i,1,tot) ++ cnt[getRank(x[i])];
if(cnt[0] >= 2){ puts("NO"); continue; }
else if(cnt[0] == 0){
puts((cnt[1]&1) ? "YES" : "NO");
continue; // one each turn
}
rep(i,1,tot) if(!getRank(x[i])){
bool win = false;
rep(j,0,(x[i]-a)>>1){
int t1 = getRank(j), t2 = getRank(x[i]-a-j);
// printf("j = %d, t1 = %d, t2 = %d\n",j,t1,t2);
if(!t1 || !t2) continue; // still loses
if((t1^t2^cnt[1]^1)&1){ // even turns
win = true; break;
}
}
puts(win ? "YES" : "NO");
break; // exactly once
}
}
return 0;
}