Official

F - Shiritori Editorial by en_translator


This problem can be solved with a DP (Dynamic Programming) parametrized by pairs of “the set of unused words” and “the last character.” Consider a DP table such that

  • \(\operatorname{dp}[S][c]\coloneqq\) \(1\) if you can win from the state where the set of unused words is \(S\subseteq\{s _ 1,\ldots,s _ N\}\) and the last character is \(c\) ; and \(0\) if you cannot.

Then, you can determine the values of the DP table as follows:

\[\operatorname{dp}[S][c]=\left\{\!\!\begin{array}{cll}1&&({}^\exists s\in S.\;s[0]=c\wedge\operatorname{dp}[S\setminus\{s\}][s[-1]]=0)\\0&&(\text{otherwise}).\end{array}\right. \]

Here, \(s[0]\) and \(s[-1]\) denotes the first and last character of \(s\), respectively. The transition of DP reflects the fact that “you will win the game if there is a word such that if you say that word, the opponent will lose.”

If there is \(c\) such that \(\operatorname{dp}[\{s_1,\ldots,s_N\}][c]\), then Takahashi can win.

In order to fill this DP table correctly, you need to fill all \(\operatorname{dp}[S\setminus\{s\}][s[-1]]\) prior to finding \(\operatorname{dp}[S][c]\). It is achieved by a typical trick of bit DP: convert \(S\) to a \(N\)-bit integer and process them in their ascending order.

Let \(\sigma\) be the size of alphabet. Since there are \(2^N\sigma\) states, and filling an element of a DP table costs \(O(N)\) time per state, so the overall time complexity is \(O(2^NN\sigma)\).

A sample code follows. We assume that \(\sigma\lt w\), where \(w\) is the word size.

#include <iostream>
#include <vector>
#include <utility>
#include <string>

int main() {
    using namespace std;

    unsigned N;
    cin >> N;

    // the first and last character of the i-th string
    vector<pair<unsigned char, unsigned char>> info(N);
    for (auto&&[first, last] : info) {
        string S;
        cin >> S;
        first = S.front() - 'a';
        last = S.back() - 'a';
    }

    // the c-th significant bit of dp[S] = whether the first player wins when unused words are S and the last character is c
    vector<unsigned> dp(1UL << N);
    for (unsigned S{1}; S < 1UL << N; ++S)
        for (unsigned c{}; c < N; ++c)
            if (1 & S >> c)
                dp[S] |= (1UL & ~dp[S ^ 1UL << c] >> info[c].second) << info[c].first;

    // if any bit of dp[2^N - 1] is set, then the first player wins
    cout << (dp.back() ? "First" : "Second") << endl;

    return 0;
}

posted:
last update: