garakutagoya

興味、関心のあったこと、そして私の気持ちなどを残していきたい

Atcoder Regular Contest 034_C_約数かつ倍数

水色の問題.そのまま約数列挙するのが不可能なものをどう見るか.

atcoder.jp

問題の説明

2個の正整数A,Bが与えられる.A!の約数であり,かつB!の倍数でもあるものの個数を1,000,000,007で割った余りを求めよ.

制約

  •  1 \le A \le B \le 10^{9}
  • 1 \le A-B \le 100

考えた事

まず階乗のscaleから考えて,どう見ても実際にA!の約数をすべて列挙してB!の倍数となっているか判定する方法は間に合わなさそうに見える.1 ここでは数式から候補を絞ってみよう.そもそもB!の倍数とはどう書けるか.

x=(B!の倍数)とすると,x=kB!(k\in\mathbb{N})となる.このxA!の約数となるから,\frac{A!}{x}は整数となる.

\frac{A!}{x}=\frac{A!}{kB!}=\frac{B!(B+1)(B+2)...A}{kB!}=\frac{(B+1)(B+2)...A}{k}より,\frac{(B+1)(B+2)...A}{k}を整数とするためのkの候補は(B+1)(B+2)...Aの約数と絞られる.

ここまで来たらもう少しだ.(B+1)(B+2)...Aの約数の個数をどうやって知ろうか.もちろん素因数分解である.一般に知られている素因数分解の計算量はO(\sqrt{N})なので,(B+1)(B+2)...Aをひとつの数とまとめてやろうとすると色々苦しい.

ここではB+1,B+2...,Aの積とみるのがいいだろう.B+1,B+2,...A素因数分解でそれぞれ得た底と指数をまとめるとそれは,(B+1)(B+2)...A素因数分解を行った結果と同様のものである.2 底と指数をまとめるツールとしてはmapを用いるのがベターだろう.mapkeyを底として,value素因数分解で得られた指数を格納する.

ここまでできたらほぼ終わり.約数の個数を求める場合の重要な性質 「ある数の約数の個数はその数を素因数分解して得られた(指数+1)の総積となる.」を活用するだけである.

これは上で実装したmapを用いて極めて簡単に計算ができる.求めたかったものはA!の約数かつB!の倍数であったからこの答えを出力してAC. ここまでお読みいただきありがとうございました.

プログラム

#include <bits/stdc++.h>
using namespace std;
#include <atcoder/all>
using namespace atcoder;
using ll = long long;
using mint = modint1000000007;
#define reps(i, a, n) for (ll i = (a); i < (ll)(n); i++)
int main() {
    ll A, B;
    cin >> A >> B;
    map<ll, ll> ma;
    reps(x, B + 1, A + 1) {
        ll res = x;
        for (ll i = 2; i * i <= x; i++) {
            ll count = 0;
            while (!(res % i)) {
                res /= i;
                count++;
            }
            if (count) {
                ma[i] += count;
            }
        }
        if (res != 1) {
            ma[res]++;
        }
    }
    mint ans = 1;
    for (auto x : ma) {
        ans *= (x.second + 1);
    }
    cout << ans.val() << endl;
}

  1. この方式を取るのが5点分のテストケースの問題.

  2. 結合律に近いニュアンス