No.109 N! mod M

  • M-N の差が小さいことを利用して計算する。
  • p^2 == M のときの罠にはまって WA した。コーナーケースの処理が甘い...。
class NFactorialModM {
public:
    void solve(void) {
            int T;
            cin>>T;

            //
            // N >= M なら N*(N-1)*...*(M+1)*M*(M-1)*...*1 = 0 mod M
            // N < M としてよい              ~~
            //
            // M-N <= 10^5 より
            // (M-1)!/N! = (M-1)*...*(N+1) は O(10^5) で計算可能
            //
            // N! = N!/(M-1)! * (M-1)! = (M-1)! * ((M-1)!/N!)^(-1) であることに留意する。
            //
            // * M が素数のときウィルソンの定理より
            //   (M-1)! == (M-1) mod M
            //
            // * M が合成数のとき M = (M/p) * p  (pは素数) とかける
            //  A. M/p <= N && p != M/p  ならば N! で M が現れるので 0
            //  B. M/p > N ならば M > p*N >= 2*N なので 10^5 > M-N > N (N! 計算すればよい)
            //  C. M == p^2 のとき
            //   * p < 2*p <= N なら N! = 0 (mod M)
            //   * p <= N < 2*p < 2*sqrt(M) < 10^5 のときは (B) として計算できる
            //
            REP(t,T)
            {
                int N,M;
                cin>>N>>M;

                if (N >= M || M == 1)
                {
                    cout<<0<<endl;
                    continue;
                }

                ll p;
                for (p = 2; p*p <= M; ++p)
                {
                    if (M%p==0)
                        break;
                }

                if (p*p > M) // M is prime
                {
                    ll a = 1;
                    for (int i = 1; i < M-N; ++i)
                        (a *= (N+i)) %= M;
                    cout<<(mod_inverse(a,M)*(M-1))%M<<endl;
                    continue;
                }

                if (M/p <= N && M != p*p)
                {
                    cout<<0<<endl;
                    continue;
                }
                if (p*p == M && 2*p <= N)
                {
                    cout<<0<<endl;
                    continue;
                }

                ll a = 1;
                for (int i = 1; i <= N; ++i)
                    (a *= i) %= M;
                cout<<a<<endl;
            }
    }
};