Official

B - 円形カード回し / Circular Card Rotation Editorial by physics0523


当然ながら、実際に \(K\) 回のカード回しをシミュレーションして時間計算量 \(O(NK)\) かけることはできません。
では、どうすればよいでしょうか?

サンプルからも察しがつくと思われますが、答えは \((1,2,\dots,N)\)\(K\) 回右シフトしたものです。
これは、剰余演算 (余りを取る演算) により効率よく求めることができます。

便宜上、扱う値の範囲を \((0,1,\dots,N-1)\) として出力の際に \(+1\) することにします。
すると、 \(i\) 個目 ( \(0 \le i < N\) ) に出力すべき値は \((i-K) \ \bmod{N}\)\(1\) を加えた値であることが分かります。
このことは、 \(1\) 度のカード回しで基本的には値が \(1\) 小さくなり、 \(0\) を持っている子どもだけ \(N-1\) を持つことになることから説明できます。

なお、ここでの \(x\ {\rm mod}\ y\) は、 \(x\)\(y\) で割った余りを表します。
但し、 \(x\) が負である場合も値が \(0\) 以上 \(y\) 未満であるようにします。例えば、 \(-1\ {\rm mod}\ 3=2, -2\ {\rm mod}\ 3=1\) です。
厳密さを捨てて説明すると、 \(1\) 円の借金を \(3\) 人で割る時に、全員に \(1\) 円の借金をつけて \(2\) 円余る (場に \(0\) 円以上 \(3\) 円未満残るようにする) ということに対応します。
厳密には、 \(x=qy+r\) かつ \(0 \le r < y\) を満たす唯一の整数組 \((q,r)\)\(r\)\(x\ {\rm mod}\ y\) と定めます。

一部の言語ではこれをそのまま記述すれば正答できますが、 C++ では剰余演算の仕様が厄介で、 \(x\%y\) の結果が \(0\) 以上 \(y\) 未満に収まる実装となっていません。
そこで、以下のように被除数 (割られる数) を非負にしてやる必要があります。

  • \(K\)\(K \% N\) として結果が変わらないので、そうする。
  • その後、 \((i+N-K)\%N\) で求解する。括弧内が常に非負であることに注意されたい。

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;
using ll=long long;

int main(){
  ll N,K;
  cin >> N >> K;
  K%=N;
  for(ll i=0;i<N;i++){
    ll x=(i+N-K)%N;
    cout << x+1 << "\n";
  }
  return 0;
}

posted:
last update: