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

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;
}



你可能感兴趣的:(codeforces)