Codeforces 455 B. A Lot of Games
[$>Codeforces \space 455 B. A Lot of Games<$](http://codeforces.com/contest/455/problem/B)
题目大意 : 有两个人在玩游戏,一共玩 \(k\) 轮,每一轮的一开始有一个空串,双方每一回合需要在空串后面加一个字符,但必须要满足加完这个字符之后的字符串是给定大小为 \(n\) 的母串集合中任意一个串的前缀,不能操作者输,规定第一轮的先手为第一个人,接下来每一轮的先手为上一轮的输家,规定最终的赢家是第 \(k\) 轮的赢家,在双方都采取最优策略的情况下,求最终的赢家是第一轮的先手还是后手.
\(1 \leq n \leq 10^5 \ 1 \leq k \leq 10^9\)
解题思路 :
先单独考虑每一轮的游戏,发现本质上就是对母串建 \(Trie\) 树并在 \(Trie\) 树上每次向下移动,不能移动的输
所以可以先 \(dp\) 出对于 \(Trie\) 树上每一个节点,能获得的最终状态是怎样的.
观察发现,如果一个位置往下走既可以到必胜态又可以必败态,那么就可以通过这个位置控制下一局的先后手
进一步发现,如果某一方既可以必胜又可以必败,那么其必然能获得最终的胜利.
考虑如果子游戏中不存在这样的状态,那么如果先手必败则后手赢,如果先手必胜则胜负由 \(k\) 的奇偶性决定
所以只需要 \(dp\) 记录维护 \(4\) 种值,分别表示 (能必胜,能必败,既能必胜又能必败,什么都不能),枚举后继的状态转移即可
/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
#define N (1000005)
const int win = 1, lose = 2, lover = 3, fucker = 4;
char s[N];
int ch[N][26], f[N], n, k, size;
inline void insert(char *s){
int p = 1, len = strlen(s);
for(int i = 0; i < len; i++){
int c = s[i] - 'a';
if(!ch[p][c]) ch[p][c] = ++size;
p = ch[p][c];
}
}
inline void solve(int u){
f[u] = lose; int cwin = 0, close = 0, all = 0;
for(int c = 0; c < 26; c++) if(ch[u][c]){
int v = ch[u][c]; solve(v), all++;
if(f[v] == lose) cwin = 1;
if(f[v] == win) close = 1;
if(f[v] == fucker) return (void) (f[u] = lover);
}
if(cwin && close) return (void) (f[u] = lover);
if(!cwin && !close && all) return (void) (f[u] = fucker);
if(cwin) f[u] = win; if(close) f[u] = lose;
}
int main(){
size = 1;
read(n), read(k);
for(int i = 1; i <= n; i++) scanf("%s", s), insert(s);
solve(1);
if(f[1] == lover) return puts("First"), 0;
if(f[1] == fucker || f[1] == lose) return puts("Second"), 0;
if(k & 1) puts("First"); else puts("Second");
return 0;
}