どんな操作?
具体的に考えてみます。
で最初 を選んだとします。
1回目の操作で、 になります。
2回目の操作で、 になります。
3回目の操作で、 になります。
4回目の操作で、 になります。
4回の操作で最初に戻るので、周期4の繰り返しになります。
また、
で最初 を選んだとします。
1回目の操作で、 になります。
2回目の操作で、 になります。
3回目の操作で、 になります。
4回目の操作で、 になります。
いったん4になると、以降はずっと4のままで1,2,3に戻ることはありません。
どちらが勝つか?
繰り返しになる部分は、指定された回だけゴールから逆方向に戻ったところをに選ぶことで、回の操作後にゴールに達することが可能です。つまり、の値にかかわらずを適切に選ぶことで高橋君の勝ちになります。
繰り返しにならない部分は、一度通り過ぎると再度訪れることはありません。そこで、最初に青木君が大きなの値を指定すると、回の操作後にゴールに達するようなは存在せず、青木君の勝ちになります。
ループとそれ以外の検出
操作による値の変化を有向グラフで表します。入ってくる辺の次数が 0 の頂点はループではありません。その頂点から出ていく辺を取り除いたときに入次数が 0 になる頂点もループではありません。これを繰り返すことでループではない頂点をすべてリストアップすることができます。残りの辺で結ばれている頂点はループになっているので、この頂点の数が答えになります。
コード例 (Julia)
function solve() # 入力 readInts() = parse.(Int,split(readline())) readInt() = parse(Int,readline()) N = readInt() A = readInts() order = zeros(Int,N) for i = 1:N order[A[i]] += 1 # A[i] の入次数 end stack = [] for i = 1:N if order[i] == 0 # 入次数が 0 の頂点 push!(stack,i) # stack 配列に追加 end end ans = N while length(stack) > 0 p = pop!(stack) ans -= 1 # 入次数が 0 の頂点の分を引く q = A[p] order[q] -= 1 # p から出ていく辺が向かう頂点の入次数を 1 減らす if order[q] < 1 push!(stack,q) # 減らした結果、入次数が 0 になったら stack 配列に追加 end end println(ans) end # function solve # main solve()