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:
