0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AtCoder ABC 186 E - Throne

Last updated at Posted at 2020-12-20

ABC 186のEがさっぱりだったので復習。

問題

AtCoder ABC 186 E - Throne

  • N個の座席が円周状に並んでいて、1つは玉座
  • 初期位置は玉座から時計回りにS個ずれている
  • 1回の移動で、時計回りにK個ずれる
  • 何回移動すれば玉座に座れる?

以下、座席の位置を0~N-1、玉座を0とする。初期位置はSで、位置0に到達するための回数を求める問題となる。

解法の概略

  • 最大公約数(GCD)で行けるかどうか判定
  • 拡張ユークリッドの互除法(通称extgcd)を用いて逆元を求める
  • 逆元×移動したい個数が答え

最大公約数と移動可能性

NとKの最大公約数(GCD)をgとする。KとNは何倍してもgの倍数なので、ループして戻ってきてもgの倍数ごとの位置となる。すなわち、最小の移動単位がgであることを意味している。
特に、gが1より大きいときについて考えると、g未満のずれを解消することはできないということである。開始位置Sからg単位でしか移動できないので、位置を0にするためにはS % g = 0(Sをgで割った余りが0)でなければならない。
なので、まずNとKの最大公約数gを求め、S % g = 0でなければ不可とし、そうでない場合は、NとKとSをgで割っておく。そうすると以降、NとKは互いに素となるので、扱いやすくなる。

移動回数と周期性

NとKが互いに素のとき、同じ場所に戻ってくる回数について考えてみる。初期位置が0で、a回移動したあとの位置bは、Nごとに1周するのでNの余りとなり、b = (a × K) % Nと表すことができる。同じ場所に戻ってくるのはb = 0、すなわちa × KがNの倍数となるときである。それはNとKの最小公倍数(LCM)ということになるが、NとKは互いに素なので最小公倍数はN × Kである。つまり、N回移動するごとに同じ場所に戻ってくる。(周期Nごとに、N個の異なる位置を取る)

逆元と任意の場所への移動

問題を少し変形して、一つ隣の位置に行くことを考えてみる。もしa回の移動で一つ隣の位置に行けるならば、b個先の位置には、a × b回の移動で行けるはずである。
この問題では位置をNの余りで考えるので、Nを法とする合同式で扱っていることになる。それをmod Nと記述する。このような世界で便利な数に逆元がある。逆元はかけると1になる数であり、x × K ≡ 1 (mod N)となるxが逆元である。これはつまり、x回移動すると位置が1進むことを意味している。すなわち、逆元が求まれば移動回数が計算できそうである。

逆元の計算

ax + by = gcd(a, b)となるx, yは拡張ユークリッドの互除法(通称extgcd)で計算できる。ここではextgcdによりKx + Ny = 1となるx, yを計算する。mod Nで考えるとNyが消えてKx ≡ 1 (mod N)となる。xがmod NにおけるKの逆元であり、x回移動すると一つ隣の位置に行ける。
サンプルにあるN=998244353, S=897581057, K=595591169で逆元を計算すると-145577299となった。ただし求めたいのは正の移動回数なので、これを直接答えには使えない。ここでN回ごとに同じ位置に戻る周期性を利用すると、-145577299 + N = 852667054回の移動で隣に行けることがわかる。

移動回数の計算

初期位置Sから位置0に移動したいので、N - S個移動する。
隣に移動するのに(x + N) % N回必要なので、((x + N) % N) × (N - S)回移動すればよい。さらに、N回ごとに同じ位置に戻るので、答えは((x + N) × (N - S)) % Nとなる。また、N回周期なのでこれが最小値となる。
サンプルにあるN=998244353, S=897581057, K=595591169だと((-145577299 + 998244353) × (998244353 - 897581057)) % 998244353 = 85832276046249984 % 998244353 = 249561088となり出力例と一致した。
よくあるextgcdの実装では戻り値としてgcdを返すので、それを利用するなら以下のようになる。(C++)

long long x, y;
long long g = extgcd(k, n, x, y);
long long ans = (((x + n) * (n - s)) % n) / g;

余談

pが素数のときはフェルマーの小定理から$ a^{p - 2}$ mod pでaの逆元が計算できるが、この問題ではNが合成数のこともあるので適用できない。

参考情報

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?