ARC 087 E - Prefix-free Game
- 問題
- 解法
根が空の文字列に対応していて、左の子には0を右の子には1を文字列に足したものを対応させる完全二分木を考えます。
そうすると良い文字列があったとき、この二分木上での先祖子孫に対応する文字列はに追加することはできません。
つまりが与えられたとき、に追加できるのはに含まれる各文字列の先祖子孫をすべて集めたもの以外となります。
このとき追加できる文字列たちは、二分木上ではいくつかの完全二分木に分かれています。
この小さな完全二分木の高さをとしたとき、二分木のgrundy数は実験してみるとを割り切る最大の2のべきとなることがわかります。
各grundy数の総xorが0ならAlice,そうでないならBobの勝ちです。
小さな完全二分木を見つけるには入力文字列から作られるTrie木を根から全探索すればいいです。
#include <iostream> #include <string> #include <algorithm> #include <cstdio> #include <vector> #include <queue> #include <set> #include <map> #include <numeric> #include <cmath> using namespace std; typedef long long int ll; #define all(x) x.begin(),x.end() const ll mod = 1e9+7; const ll INF = 1e9; const ll MAXN = 1e9; struct trie{ bool leaf; trie* node[2]; trie(){ leaf = false; for(int i = 0; i < 2; i++){ node[i] = (trie*)0; } } void insert(const string &s){ trie* r = this; for(int i = 0; i < s.length(); i++){ int c = s[i]-'0'; if(!r->node[c]) r->node[c] = new trie; r = r->node[c]; } r->leaf = true; } }; ll ans = 0; ll grundy(ll x){ if(x== 0) return 0; ll res = 1; while(x%(res*2)==0) res*=2; return res; } void solve(trie *t,ll d,ll l){ if(t->leaf || d>=l) return; if((!t->node[0]&&t->node[1]) || (!t->node[1]&&t->node[0])){ ans ^= grundy(l-d); } for(int i = 0; i < 2; i++){ if(t->node[i]) solve(t->node[i],d+1,l); } return; } int main() { ll n,l; cin >> n >> l; trie t; for(int i = 0; i < n; i++){ string s; cin >> s; t.insert(s); } solve(&t,(ll)0,l); cout << (ans!=(ll)0?"Alice":"Bob") <<endl; return 0; }