競技プログラミングでは、10の9乗足す7で割った余りを求めるような問題が多く存在している。 それは答えが大きくなる問題についての対処であり、 なぜ10の9乗足す7なのかというとその数が素数だからである。
この手の問題は多くある上にわざわざ何回も同じプログラムを書くのは面倒なので、ライブラリみたいな形として残そうと思ったのがこの記事を書こうと思ったきっかけである。 アルゴリズムのアイディア自体は自分が考えたものではないのでそこは容赦してほしい。 また、言語はPython3を使用している。
剰余の法を変えるためには、変数characteristicの値を変更すれば良い。ただし変更したあとの値が素数でないとバグが発生する(未確認だけど) また、divideはexponenとtimesが両方定義されていないと使えないので、仮にコピペする場合はソースコードまるごと貼り付けたほうが無難。このコード自体は関数の宣言だけで終わっているので実行しても何も起こらない。
2018年4月28日に行われたAGC023(Atcoder Grand Contest 023)のCの問題についてなのだが、その問題を解くときに階乗の$P=10^9+7$に対する逆元を求める操作を行う。 ただし$N\leq 10^6$の条件では上記の手法で逆元を求めると(自分の環境では)30秒以上かかってしまったのである。そもそも階乗を$P$で割った余りを$10^6$までで求める行為ですら0.5秒かかってしまうので、 逆元ではその60倍の30秒ほどかかってしまいTLEとなる。これを避けるためには逆元の求め方を変えなくてはいけないのである。フェルマーの小定理ではなく、ユークリッドの互除法(不定方程式)を使用して余りを求めた方が速いのである。 さらにその条件のもとで言語をPython3からPyPy3に変えなくてはACすることができない。(ACしたもののアドレスはこれである。)競技プログラミングをPython3でやるようになって5ヶ月以上経ったのだが、DFSで呼び出し制限にかかったり、(C++と比べた上での)実行速度の遅さから 少しずつ限界を感じ始めているところである。やはりC++への乗り換えが求められているのだろうと思う日々である。