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