はまやんはまやんはまやん

hamayanhamayan's blog

Teleporter [AtCoder Beginner Contest 167 D]

https://atcoder.jp/contests/abc167/tasks/abc167_d

前提知識

解説

https://atcoder.jp/contests/abc167/submissions/13093461

初めてこの手の問題に取り組んだ人は、方針にだいぶ迷ったかもしれない。
今回の方針としては、シミュレーションの高速化が方針になる。

愚直にK回の遷移を試すと、最大1018回なので、それは難しい。
なので、+1+1+1というのをやっていくと間に合わないが、ダブリングという手法を使うと、
+1+2+4のように遷移を行うことができ、シミュレーションを高速化できる。
以下ダブリングを簡単に説明するが、他のサイトで勉強した方が分かりやすいと思う。

ダブリングでは、以下のdpを使う。
dp[p][cu] := 現在頂点cuにいて、そこから2p回遷移したときの遷移先
最初はもちろんdp[0][cu] = A[cu]からスタートする。
dpの遷移はdp[p][cu] = dp[p-1][ dp[p-1][cu] ]で更新することができる。
これは難しく見えるかもしれないが、
 dp[p-1][cu]で頂点cuから2p-1回遷移したときの遷移先
であり、
 dp[p-1][ dp[p-1][cu] ]
とすると、これは、そこから更に2p-1回遷移したときの遷移先となる。
2p-1 + 2p-1は2pなので、2p回遷移した先を代入することができる。

K=1018では、260までくらいを作ればいいので、dpは作成することができる。
これを使えば、K回の遷移はビットが立っている部分について、ダブリングのdpテーブルを使って遷移をすればいいので、
シミュレーションを高速化でき、K回後の頂点を答えることができる。

なお、別解として周期性を用いる方法がある。
今回を有向グラフとして考えると、このグラフは有向なもりグラフとなる。
これは、遷移させていくと、ループが1つあることが知られており、ループによる周期性を用いて、
シミュレーションの高速化をすることもできる。
細かな話題

int N; ll K; int A[201010];
int dp[61][201010];
//---------------------------------------------------------------------------------------------------
void _main() {
    cin >> N >> K;
    rep(i, 0, N) cin >> A[i], A[i]--;

    rep(cu, 0, N) dp[0][cu] = A[cu];
    rep(p, 1, 61) rep(cu, 0, N) dp[p][cu] = dp[p - 1][dp[p - 1][cu]];

    int cu = 0;
    rrep(p, 60, 0) if (K & (1LL << p)) cu = dp[p][cu];
    cout << cu + 1 << endl;
}