Codeforces Round #354 (Div. 2) E. The Last Fight Between Human and AI
問題
A さんと B さんが N 次の多項式の係数を埋めるゲームをする(係数は実数ならなんでも良い)。
A さんの目的はこの多項式が x-k で割り切れるようにすることで, B さんはそれを阻止することが目的である。
B さんが先行ですでにいくつかの係数が埋められている。この後 A さんと B さんが最適に動いた場合, 勝者はどちらになるか?
解法
因数定理より, x-k で多項式が割り切れるというのは, f(k) = 0 となることです。
それを考えると, 最終ターンの人が 係数を適当に調整すれば必ず f(k) = 0 とすることができそうなので, (N+1) の偶奇で勝者が決まりそうです。
ただし, 例外が二つあります。
k = 0 の場合
この場合は, 0 次の係数(a[0] とする)以外は関係ないので, そこを先にいじれた人が勝ちです。
a[0] = 0 の時, A 君の勝ちです。
a[0] がまだ決まっていないとき, 次の手番の人が勝ちです。
a[0] != 0 の時, B 君の勝ちです。
すでにすべての係数が埋まっている場合
この場合は, すでに勝敗が決まっているので, どちらが勝ったのかを調べましょう。
ただ, f(k) を直接計算すると数が大きすぎて死ぬので, 50 個ほど乱数を発生させて, それらで割ったあまりがすべて 0 だったら f(k) = 0, と判定することにします。
const int MAXN = 100100; const int CHECK = 50; string a[MAXN]; std::random_device rnd; ll mod[CHECK], p[CHECK], sum[CHECK]; bool solve() { int n, k; cin >> n >> k; int turn = 0; for (int i = 0; i <= n; i++) { cin >> a[i]; if (a[i] == "?") turn++; } if (k == 0) { if (a[0] == "0") return true; if (a[0] == "?") return (n+1-turn)%2 == 1; return false; } if (turn == 0) { for (int i = 0; i < CHECK; i++) { mod[i] = 1000000000+rnd()%1000000; if (mod[i]%2==0) mod[i]++; p[i] = 1; } for (int i = 0; i <= n; i++) { ll num = 0; if (a[i][0] == '-') { for (int j = 1; j < a[i].size(); j++) num = 10*num+(a[i][j]-'0'); num *= -1; } else { for (char c : a[i]) { num = 10*num+(c-'0'); } } for (int j = 0; j < CHECK; j++) { sum[j] += p[j]*num%mod[j]; sum[j] %= mod[j]; (p[j] *= k) %= mod[j]; } } for (int i = 0; i < CHECK; i++) { if (sum[i]) return false; } return true; } if ((n+1)%2==0) return true; return false; } int main() { cin.tie(0); ios::sync_with_stdio(false); if (solve()) cout << "Yes" << endl; else cout << "No" << endl; return 0; }