CF 260 DIV2 D. A Lot of Games(字典树+博弈)

题意:给你n个字符串,所有的字符串的长度加起来不超过10^5,两人来构造一个字符串,首先该字符串是为空,然后两人轮流选一个字母加在该字符串的尾部,要求两人构成的字符串是n个字符串中某个字符串的子串,没法继续选字母者败。 给你一个K,表示将该游戏进行K次,其中第一次游戏First先手,后面的k-1次的先手为第上次游戏的败者

例如,第一次First胜,则第二次游戏Second先手

求第K次游戏的胜者

 

方法:

将n个字符串存在字典树,然后遍历整棵树,其中每个结点状态的可能性有四种 1(表示改点必胜),2(表示改点必败),3(表示改点想败就败,想胜就胜),4(表示改点可能败,也可能胜,胜负由对手控制)

遍历树的过程时:

if(改点为树的叶子节点) ,改点状态为2  (因为无法再选择字母)

else  if  某点的子节点中有状态为4的节点,则改点的状态为3 (因为只要在该点的人走到4,就可以控制对手的胜负,所有改点的状态为3)

当改点不是叶子节点且改点的字节点没有状态为4的点时:

  if  改点的子节点有状态为1还有状态为2的子节点,则改点的状态为3(如果还有子节点有状态为3的节点,该点的状态也是3,因为只要该点不朝状态为3的点那边走即可)

else if 该点的子节点有状态为1且没有状态为2的子节点,则该点的状态为2  (如果还有子节点有状态为3的节点,该点也是必败,因为如果到达该点的人朝状态为3走,表示该

人该把想赢(想输的话走状态为1的子节点就可以了),但是朝3走之后,另一个人还是可以让他绝对输,所以该点状态为2)

else if 改点没有状态为1的点有状态为2的子节点,则改点的状态为1(理由同上)

else if 改点的子节点只有状态为3的子节点,改点的状态为4 (因为只要该点走到3,对手就能够想赢就赢,想输就输)

 

知道根节点的状态ans后进行判断:

若ans= 1,可得K把游戏中,First和Second两人轮流赢,则判断K的奇偶性就可知道第K把谁用

若ans= 2,可得K把游戏中,First每把都是先手,每把都输,则结果为Second胜

若ans=3,可得First第一把就可控制胜负,所有他只要一直控制K-1把让自己输,第K把赢即可

若ans=4, 可得胜负由对手控制,同上理可得,结果为Second胜

 

代码:

#include <cstdio>
#include <cstring>
#define maxn 100005

int  trie[maxn][27];
char ch[maxn];

int dfs(int n)
{
	if(n== -1)
		return 1;
	int f1= 0, f2= 0, f3= 0, f4= 0;
	int sum= 0;
	for(int i= 1; i<= 26; i++)
	{
		if(trie[n][i]== -1)continue;
		sum++;
		int res= dfs(trie[n][i]);
		if(res== 1)
			f1= 1;
		else if(res== 2)
			f2= 1;
		else if(res== 3)
			f3= 1;
		else if(res== 4)	
			f4= 1;
	}
	if(sum== 0)
		return 2;
	if(f4) 
		return 3;	
	if(f1)
	{
		if(f2)
			return 3;//可胜可负 
		else
			return 2; 			
	}
	else if(f2)
		return 1;
	else 
		return 4;	
}

int main()
{
	int n, k;
	while(scanf("%d %d",&n,&k)!=EOF)
	{
		memset(trie, -1, sizeof(trie));
		int tot= 0; //树中节点的个数 
		for(int i= 1; i<= n; i++)
		{
			scanf("%s",ch+1);
			int len= strlen(ch+1);
			int tt= 0; //当前的节点
			for(int j= 1; j<= len; j++)
			{
				int k= ch[j]- 'a'+1;
				if(trie[tt][k]== -1)
				{
					trie[tt][k]= ++tot;
					tt= tot;
				}
				else
					tt= trie[tt][k];	
			}
		}
		int ans= dfs(0);
		if(ans== 1)
		{
			if(k&1)
				printf("First\n");
			else
				printf("Second\n");	
		}
		else if(ans== 2 || ans== 4)
			printf("Second\n");
		else if(ans== 3)
			printf("First\n");	
	}
	return 0;
} 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值