ABC296 E - Transition Game
問題概要
長さ
高橋君,青木君は、
- 青木君は、正整数
を指定する。 K - 高橋君は、青木君の選んだ
を聞いたうえで、 K 以上 1 以下の正整数 N を選び、ひとつ黒板に書く。 s - その後、以下の操作を
回繰り返す: K
黒板に書かれている数をとしたとき、黒板に書かれていた整数を消し、新たに x を書き込む。 A_x
回の操作を終えた後、書かれていた回数が K であれば高橋君の勝利,そうでなければ青木君の勝利 i
なお、毎ゲーム開始時,黒板には何も書かれていない。
両者が自身が勝つために最適に行動したとき、高橋君は最大何回のゲームで勝利することができますか?
1\leq N\leq 2\times10^5 1\leq A_i\leq N
解法
言い換え
ゲームの内容が分かりづらい...というか、両者にとって「最適な行動」が分からない。
いろいろ観察してみよう...
特徴的なのは、
動きをわかりやすくするために、
- 各頂点の出次数はちょうど
1 - 「黒板に
が書かれた状態から初めて、x 回の操作を行った後に書かれている数」k
は、
「 においてG からx 進んだ頂点の番号」k
と同じ。
これを使うと、高橋君は次の戦略をとればよい:
から i 回さかのぼった頂点が存在すればそれを K に指定する s
例
例えば
例えば、
他にも、
一方、そのような頂点が存在しないこともある。例えば、
観察
グラフを観察してみると、次のような性質が分かる。
基本的には
をめちゃくちゃでかくとられると、 K ステップ遡ることはできない。 K
例
例えば
この時、
ただ、これを回避できる状況が存在する!!!
それは、
逆にそうじゃないときは、上の戦略により遡れなくすることが可能となってしまう...
結論
ということで、
したがって、
実装
与えられるグラフは Functional Graphなので、SCC(強連結成分分解)を使うと楽に実装できる。
具体的には、
- SCCする
- 各成分を見る。次のいずれかを満たしていれば、その成分のサイズを答えに加算する
- サイズが
で、かつ自分から自分への辺がある1 - サイズが
以上2
- サイズが
#include<bits/stdc++.h>
using namespace std;
#include<atcoder/all>
int main(){
int n;
cin >> n;
atcoder::scc_graph g(n);
vector<int> to(n);
for (int i = 0; i < n; i++) {
int a;
cin >> a;
a--;
to[i] = a;
g.add_edge(i, a);
}
auto res = g.scc();
int ans = 0;
for (auto& vec : res) {
if (vec.size() >= 2){
ans += vec.size();
}else{
int v = vec.front();
if (to[v] == v) {
ans++;
}
}
}
cout << ans << '\n';
}
Discussion