面白い解法で解けたので (流石に既出かもしれない)
解法
これだけです.何をしているかというと,数列の後ろから順番に同じ場所を 回ずつ連続して選択しています.
順列 を読まなくても解けるのが個人的に面白いと思った部分です.
N = int(input()) print(N * (N - 1)) for i in range(N): for _ in range(N - 1): print(N - i - 1)
提出すると確かに AC を取れますが,本当に正しいのでしょうか?
正当性
まず,添え字 を連続して選択したときの の値の変化を考えます.
少し実験すると,
という性質がありそうだと気づきます.
のとき,添え字 を選択すると の位置は へと移ります.従って,再び となるためには, となる必要があります.そして,これを満たすのは に限られます.つまり,2 回以上同じ値となるのは しかありえないということになり, は正しいです.
次に,外側のループが一つ進むごとに順列がどう変化しているかを見てみます.以下,例として を用います.
N = 6 P = [0, 2, 3, 5, 4, 1] def swap(i): j = (i + P[i]) % N P[i], P[j] = P[j], P[i] for i in range(N): for _ in range(N - 1): swap(N - i - 1) print(P)
外側のループ毎に順列の状態を出力するコードです.以下に出力を示します.
[1, 2, 3, 5, 4, 0] [2, 3, 4, 5, 0, 1] [3, 4, 5, 0, 1, 2] [4, 5, 0, 1, 2, 3] [5, 0, 1, 2, 3, 4] [0, 1, 2, 3, 4, 5]
回目のループが終了した時点で, となっていそうです.これを以下の 2 ステップに分けて示します.
- の場合に正しいこと
- の場合に正しいと仮定すると, の場合も正しいこと
の場合は, より添え字 を 回連続して選択した後は必ず となっていることから従います.
の場合に正しいことを仮定すると, です.
添え字 を連続して選択した場合に,swap 相手の添え字が初めて より大きくなるタイミングを考えます.仮定より, を満たす全ての に対して が成立します.従って,これは となったタイミングに限られます.そして,
- となるまでは となりえないこと
- の性質
の 2 つから, となるまでの最大の操作回数は 回で上から抑えることが出来ます.更に, となった直後の 回の操作で となることから, の場合も正しいことが言えます.
以上より,冒頭に示した解法は正当であることが言えました.