题解:AT_abc209_e [ABC209E] Shiritori
解题思路
考虑利用拓扑序逆推答案。
对于每一个单词,显然,只用考虑它的前 \(3\) 个字母和后 \(3\) 个字母。可以对每一种组合都编一个编号。
设当前字母组合为 \(s\),那么编号为 \(mp_s\)。
建立一张图,从每个单词的后 \(3\) 个字母组成的字符串向前 \(3\) 个字母组成的字符串连一条边,并将节点 \(mp_{front}\) 的入度增加 \(1\)。
然后根据拓扑序计算答案。
定义一个队列 \(q\),如果当前节点的入度为 \(0\),就把当前节点放到队列里面,并把状态设为必胜态。
对与每一个节点,如果它的前一个节点是必胜态,那么它就是必败态,反之亦然。
最后输出答案即可。
核心代码
建图:
rep(i, 1, n) {
cin >> s[i];
string t = "", tt = "";
t += s[i][0], t += s[i][1], t += s[i][2];
if (!mp[t]) mp[t] = ++ cnt;
tt += s[i][s[i].size() - 3], tt += s[i][s[i].size() - 2], tt += s[i][s[i].size() - 1];
if (!mp[tt]) mp[tt] = ++ cnt;
g[mp[tt]].push_back(mp[t]);
d[mp[t]] ++ ;
}
计算答案:
// 1 表示 Takahashi 必胜,-1 表示 Aoki 必胜,0 表示平局
queue<int> q;
rep(i, 1, cnt) if (!d[i]) {
q.push(i);
f[i] = 1;
}
while (!q.empty()) {
int u = q.front();
q.pop();
rep(i, 0, g[u].size() - 1) {
int ver = g[u][i];
d[ver] -- ;
if (f[ver] == 0 && f[u] == 1) {
f[ver] = -1;
q.push(ver);
}
if (f[ver] == 0 && f[u] == -1 && !d[ver]) {
f[ver] = 1;
q.push(ver);
}
}
}