[codeforces1221E]-Game With String-博弈

说在前面

把题读错了。。。
对自己感到无语TvT


题目

codeforces1221E传送门

题目大意

Alice和Bob在玩游戏
现在给出一个字符串,其中含有’X’和’.’(不带引号)
每次操作,Alice可以把A个连续的’.‘变成’X’,Bob可以把B个连续的’.‘变成’X’(保证A>B),谁走不动谁输
如果双方采取最优策略,问最后Alice是否能赢

多组数据,字符串总长度 ≤ 3 × 1 0 5 \leq 3\times 10^5 3×105


解法

首先Alice和Bob的操作是不同的,因此不是组合游戏,不能用SG那一套理论,需要从寻找必胜态入手

(然后没想出来,看了题解发现我题读错了)

我们取出序列中连续的" . . .",将其长度记为len
并约定:
l e n < B len<B len<B 为1类串
B ≤ l e n < A B\leq len<A Blen<A 为2类串
A ≤ l e n < 2 × B A\leq len<2\times B Alen<2×B 为3类串
2 × B ≤ l e n 2\times B \leq len 2×Blen 为4类串

不难发现,如果序列中出现一个2类串,那么Alice必定输(因为A>B,如果Alice走不动了,Bob至少还能走一步2类串)。进一步的,如果有两个及以上的4类串,Bob一定能够造出一个2类串,Alice必输。

如果没有4类串,那么输赢由3类串个数的奇偶决定

如果恰好只有一个4类串,Alice的先手就需要保证她能赢,所以判断一下这个4类串在被Alice操作一次之后,能否出现先手必输的局面,这个根据3类串的奇偶以及4类串的长度特判就行了


下面是代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

// A > B
int T , A , B , B2 , sta[300005] , topp , slen ;
char ss[300005] ;

void preWork(){
	topp = 0 ;
	slen = strlen( ss + 1 ) , B2 = B * 2 ;

	int L = 1 , R ;
	while( L <= slen ){
		while( ss[L] == 'X' ) L ++ ;
		R = L ;
		while( ss[R] == '.' ) R ++ ;
		if( R - L >= B ) sta[++topp] = R - L ;
		L = R ;
	} sort( sta + 1 , sta + topp + 1 ) ;
}

bool cut_1_or_3( int len ){
	if( len <= A + 2 * B - 2 ) return true ;
	if( A * 3 <= len && len <= A + B2 * 2 - 2 ) return true ;
	return false ;
}

bool cut_1_and_3( int len ){
	if( A + A <= len && len <= B - 1 + A + B2 - 1 ) return true ;
	return false ;
}

void solve(){
	int cnt2 = 0 , cnt3 = 0 , cnt4 = 0 ;
	for( int i = 1 ; i <= topp ; i ++ ){
		if(                sta[i] < A  ) cnt2 ++ ;
		if( A <= sta[i] && sta[i] < B2 ) cnt3 ++ ;
		if( B2<= sta[i]                ) cnt4 ++ ;
	}
	if( cnt2 || cnt4 >= 2 ){ puts( "NO" ) ; return ; }

	if( !cnt4 ){ puts( ( cnt3 & 1 ) ? "YES" : "NO " ) ; return ; }

	if( ( cnt3 & 1 ) && cut_1_and_3( sta[topp] ) == true ){ puts( "YES" ) ; return ; }

	if( !( cnt3 & 1 ) && cut_1_or_3( sta[topp] ) == true ){ puts( "YES" ) ; return ; }

	puts( "NO" ) ;

}

int main(){
	scanf( "%d" , &T ) ;
	while( T -- ){
		scanf( "%d%d" , &A , &B ) ;
		scanf( "%s" , ss + 1 ) ;
		preWork() ; solve() ;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值