[CF1221E]Game With String

27 篇文章 0 订阅

题目

传送门 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值