【East!模拟赛】【Round1】【codeforces455B】题解。

8 篇文章 0 订阅
5 篇文章 0 订阅

A:codeforces Round#260 div1 B [a lot of games].

题解:俩人玩游戏,有若干个字符串,每一轮都是俩人轮流念一个字母,使得当前的这些字母是其中一个字符串或者其前缀,即在字典树上走,每人走一步,走不了的人输,然后有m轮,每轮输的下一轮先手,问最后一轮谁赢?

题解:

    显然这是一道博弈题,但是如果我们单纯地计算每一局是先手赢还是后手赢,那就要跪了。

    因为:先手可以选择输,以保证下一局的先手,然后一直先手,最后一局再选择赢!!!

    那么我们就需要多判断几种状态。

    首先有一种朴素的算法(不仅朴素还难写),是树上每个点枚举9种状态,即:

1.可以赢,可以输。

2.可以赢,输不输看对手。

3.可以胜,一定输。

4.赢不赢看对手,可以输。

……

一共九种,赢的一面有三种:必胜,看对手,必败,输的同理,乘一下9种。

当然,其中我们可以剪掉一些,但是!!依然很难写!!!


于是就有了进一步思考后的分析。

我们可以分成四种状态,即可胜可负,必胜,必败,胜负都看对手。

然后分别对应3,2,1,0

这样可以ans|=dfs(v)^3;

满足条件。

为什么可以这样呢?这真的对么?这真的对。要不codeforces那么强力的hack也过不了~~


正确性:3、2、1应该都不用说了。要说的应该只有0状态。

0状态:

    首先游戏是正向进行的,,虽然DP是从叶子开始推的。。

    然后某人进行选择,那么他的对手就可以有针对性地进行选择,不让这个“某人”如愿以偿,要么是怒赢,要么是故意输然后拿先手。

    正确性证明完毕。


然后总体思路就是建Trie树,然后得出先手是0、1、2、3哪种状态,然后就可以O(1)出解了~~

贴代码:(要在codeforces上交需要删freopen)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 101000
#define T 130
using namespace std;
char s[N];
int n,m;
struct Trie
{
	int next[220000][T],cnt;
	void insert()
	{
		int i,x,alp;
		scanf("%s",s);
		for(i=x=0;s[i];i++)
		{
			alp=s[i];
			if(!next[x][alp])next[x][alp]=++cnt;
			x=next[x][alp];
		}
		return ;
	}
	int dfs(int x)
	{
		int i,v,ans=0;
		bool flag=1;
		for(i=0;i<T;i++)if(v=next[x][i])
		{
			flag=0;
			ans|=dfs(v)^3;
		}
		if(flag)return 1;
		return ans;
	}
}trie;
int main()
{
	freopen("A.in","r",stdin);
	freopen("A.out","w",stdout);
	int i,ans;
	scanf("%d%d",&n,&m);
	if(m==0)puts("Sword!");
	else
	{
		for(i=1;i<=n;i++)trie.insert();
		ans=trie.dfs(0);
		if(ans==3||(ans==2&&m&1))puts("Sword!");
		else puts("Oh!no!");
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值