H - Transition Game 解説
by
shakayami
ダブリングを使った実装方法
evimaさんの解説にあるように、 \(i\to A_i\)に辺を張ったときのサイクル上にある頂点の個数を求める問題に帰着されます。
結論から言うと以下のような実装でACすることができます。
N=int(input())
A=[int(i)-1 for i in input().split()]
for i in range(30):
A=[A[a] for a in A]
print(len(set(A)))
\(i\to A_i\to A_{A_i}\to A_{A_{A_i}}\to A_{A_{A_{A_i}}}\to \cdots \)と辿った場合、\(i\)の出発点をどこにしても何回か辿ったらサイクルにたどり着きます。
証明
\(x_0=i,x_{n+1}=A_{x_n}\)であるような数列を考えます.
このとき、\(x_n\)は\(1\)以上\(N\)以下の\(N\)通りの値しか取りえません。
そこで、先頭の\(N+1\)項\(x_0,x_1,\ldots,x_N\)を考えると、鳩の巣原理でどこか一致するペアが存在します。それを\(x_a,x_b(a\lt b)\)とします。
このとき、 \(x_a\to x_{a+1}\to \cdots x_{b-1}\to \) は長さ\(b-a\)のサイクルとなります。
よって\(x_n\)は\(n=a\)以降は \(x_a\to x_{a+1}\to \cdots x_{b-1}\to x_a\to x_{a+1}\to \cdots x_{b-1}\to\cdots \) の繰り返しとなるため常にサイクル上に存在することとなります。
証明より、\(N+1\)回以上の十分大きい回数を辿ったら必ずサイクル上の点となることがわかります。
(注:サイクルが一つだけとは限りません。サイクルが\(p\)個ある場合、\(p\)個全てのサイクルのサイズの総和を求めることとなります。)
\(A_k\leftarrow A_{A_k}\)というような変換をした場合、\(A_k\)は\(k\)からちょうど2本辺を辿ったときの行き先となります。
その状態でさらに\(A_k\leftarrow A_{A_k}\)という変換をしたときには\(A_k\)は\(k\)からちょうど辺を4本辿ったときの行き先となります
同様な操作を30回ほど繰り返したとき、\(A_k\)は\(k\)からちょうど辺を\(2^{30}\)本辿ったときの行き先となります。
ここで、制約より\(2^{30}\gt N\)なので、この時点で更新された後の\(A_i\)において、全ての\(i\)について\(A_i\)はサイクル上にある頂点となります。
全てのサイクルの点\(j\)について\(A_i=j\)となるような\(i\)が存在するかどうかですが、結論を言うとこれは必ず存在します。
サイクル\(y_0,\ldots,y_{k-1}\)があったとき、それぞれから\(m\)回辿った先は \(y_i\to y_{(i+m)\% k}\)となります。 ここで、
\[\{y_i;0\leq i\lt k\}=\{y_{(i+m)\%k};0\leq i\lt k\} \]
と2つの集合は等しくなります。よって、サイクル上の点は全て網羅されることが言えます。
つまり、サイクル上の点の移動先は常にサイクル上に存在して、かつすべて異なるため、全てちょうど一つずつ網羅されます。
さらにサイクルにない頂点は移動先がサイクルの点となるため、集合として重複を消せば答えを数え上げることができます。
投稿日時:
最終更新: