[CF1221E]Game With String

题目

传送门 to CF

思路

我不得不发出这样的感慨:游戏实在是太不公平了!

显然每个 . 极长段可以单独考虑。但是,最神奇的事莫过于:只要存在 b ⩽ l e n < a b\leqslant len<a blen<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 blen<a 可以操作,胜。

同理,当存在 l e n ⩾ 2 b len\geqslant 2b len2b 的段时, 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 alen<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 alen<2b

所以我们可以想想 A l i c e \sf Alice Alice 该干什么。如果存在 b ⩽ l e n < a b\leqslant len<a blen<a 或者 l e n ⩾ 2 b len\geqslant 2b len2b,她就必须要去处理;如果有多个,直接完蛋。仅有一个,则可以枚举她的操作是什么,然后看看 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;
}

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/122444000