Problem p02793 (C++)

Buggy submission (status: Runtime Error):
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
using ld = long double;
using pll = pair<ll, ll>;
using pld = pair<ld, ld>;
const int INF = 1e9 + 7;
const ll LINF = 9223372036854775807;
const ll MOD = 1e9 + 7;
const ld PI = acos(-1);
const ld EPS = 1e-10; // 微調整用（EPSより小さいと0と判定など）

int ii() {
  int x;
  if (scanf("%d", &x) == 1)
    return x;
  else
    return 0;
}
long long il() {
  long long x;
  if (scanf("%lld", &x) == 1)
    return x;
  else
    return 0;
}
string is() {
  string x;
  cin >> x;
  return x;
}
char ic() {
  char x;
  cin >> x;
  return x;
}
void oi(int x) { printf("%d ", x); }
void ol(long long x) { printf("%lld ", x); }
void od_nosp(double x) { printf("%.15f", x); } // 古い問題用
void od(double x) { printf("%.15f ", x); }
// long doubleで受け取り、fをLfなどに変えて出力すると、変な数値が出る
// それをなんとかするには独自の出力を作らなければならなそう
void os(const string &s) { printf("%s ", s.c_str()); }
void oc(const char &c) { printf("%c ", c); }
#define o_map(v)                                                               \
  {                                                                            \
    cerr << #v << endl;                                                        \
    for (const auto &xxx : v) {                                                \
      cout << xxx.first << " " << xxx.second << "\n";                          \
    }                                                                          \
  } // 動作未確認
void br() { putchar('\n'); }

// #define gcd __gcd //llは受け取らない C++17~のgcdと違うので注意
// int lcm(int a, int b){return a / gcd(a, b) * b;}
#define begin_end(a) a.begin(), a.end() // sort(begin_end(vec));
#define REP(i, m, n) for (ll i = (ll)(m); i < (ll)(n); i++)
#define DREP(i, m, n) for (ll i = (ll)(m); i > (ll)(n); i--)
#define rep(i, n) REP(i, 0, n)
#define m_p(a, b) make_pair(a, b)
#define SORT_UNIQUE(c)                                                         \
  (sort(c.begin(), c.end()),                                                   \
   c.resize(distance(c.begin(), unique(c.begin(), c.end()))))
#define p_b push_back
#define SZ(x) ((ll)(x).size()) // size()がunsignedなのでエラー避けに
#define endk '\n'

// coutによるpairの出力（空白区切り）
template <typename T1, typename T2>
ostream &operator<<(ostream &s, const pair<T1, T2> &p) {
  return s << "(" << p.first << " " << p.second << ")";
}
// coutによるvectorの出力（空白区切り）
template <typename T> ostream &operator<<(ostream &s, const vector<T> &v) {
  int len = v.size();
  for (int i = 0; i < len; ++i) {
    s << v[i];
    if (i < len - 1)
      s << " "; //"\t"に変えるとTabで見やすく区切る
  }
  return s;
}
// coutによる多次元vectorの出力（空白区切り）
template <typename T>
ostream &operator<<(ostream &s, const vector<vector<T>> &vv) {
  int len = vv.size();
  for (int i = 0; i < len; ++i) {
    s << vv[i] << endl;
  }
  return s;
}

// 最大値、最小値の更新。更新したor等しければtrueを返す
template <typename T> bool chmax(T &a, T b) { return (a = max(a, b)) == b; }
template <typename T> bool chmin(T &a, T b) { return (a = min(a, b)) == b; }

// 4近傍（上下左右） rep(i, 2) にすると右・下だけに進む
vector<int> dx_4 = {1, 0, -1, 0};
vector<int> dy_4 = {0, 1, 0, -1};

// -------- template end - //

// - library ------------- //

// modint: mod 計算を int を扱うように扱える構造体
template <int MOD> struct Fp {
  long long val;
  constexpr Fp(long long v = 0) noexcept : val(v % MOD) {
    if (val < 0)
      val += MOD;
  }
  constexpr int getmod() { return MOD; }
  constexpr Fp operator-() const noexcept { return val ? MOD - val : 0; }
  constexpr Fp operator+(const Fp &r) const noexcept { return Fp(*this) += r; }
  constexpr Fp operator-(const Fp &r) const noexcept { return Fp(*this) -= r; }
  constexpr Fp operator*(const Fp &r) const noexcept { return Fp(*this) *= r; }
  constexpr Fp operator/(const Fp &r) const noexcept { return Fp(*this) /= r; }
  constexpr Fp &operator+=(const Fp &r) noexcept {
    val += r.val;
    if (val >= MOD)
      val -= MOD;
    return *this;
  }
  constexpr Fp &operator-=(const Fp &r) noexcept {
    val -= r.val;
    if (val < 0)
      val += MOD;
    return *this;
  }
  constexpr Fp &operator*=(const Fp &r) noexcept {
    val = val * r.val % MOD;
    return *this;
  }
  constexpr Fp &operator/=(const Fp &r) noexcept {
    long long a = r.val, b = MOD, u = 1, v = 0;
    while (b) {
      long long t = a / b;
      a -= t * b;
      swap(a, b);
      u -= t * v;
      swap(u, v);
    }
    val = val * u % MOD;
    if (val < 0)
      val += MOD;
    return *this;
  }
  constexpr bool operator==(const Fp &r) const noexcept {
    return this->val == r.val;
  }
  constexpr bool operator!=(const Fp &r) const noexcept {
    return this->val != r.val;
  }
  friend constexpr ostream &operator<<(ostream &os, const Fp<MOD> &x) noexcept {
    return os << x.val;
  }
  friend constexpr Fp<MOD> modpow(const Fp<MOD> &a, long long n) noexcept {
    if (n == 0)
      return 1;
    auto t = modpow(a, n / 2);
    t = t * t;
    if (n & 1)
      t = t * a;
    return t;
  }
};

using mint = Fp<MOD>;

// エラトステネスの篩に基づく高速な素因数分解
// 前処理したあと O(logN)でNを素因数分解
// 以下のページのライブラリを改造したもの
// https://qiita.com/rsk0315_h4x/items/ff3b542a4468679fb409
class smart_sieve {
  ll L, R, M;
  std::vector<int> small;             // 小さい篩
  std::vector<std::vector<ll>> large; // 大きい篩
  std::vector<ll> aux;                // aux[i] := large[i] の素因数の積

public:
  smart_sieve(ll L, ll R) : L(L), R(R), M(sqrt(R) + 1) {
    small.resize(M);
    std::iota(small.begin(), small.end(), 0);
    large.resize(R - L);
    aux.assign(R - L, 1);

    for (ll i = 2; i * i < R; ++i) {
      if (small[i] < i)
        continue;
      small[i] = i;
      for (ll j = i * i; j < M; j += i)
        if (small[j] == j)
          small[j] = i;

      for (ll j = (L + i - 1) / i * i; j < R; j += i) {
        ll k = j;
        do {
          // aux[j-L] > M で判定した方がいいかも？
          // j / aux[j-L] < M の方がいい？（割り算したくない）
          if (aux[j - L] * aux[j - L] > R)
            break;

          large[j - L].push_back(i);
          aux[j - L] *= i;
          k /= i;
        } while (k % i == 0);
      }
    }
  }

  // 素因数分解 O(logN)
  // factor(i) : i の素因数vector : f(120) = 2 2 2 3 5（ソート済）
  // 0 を渡すと おかしい答えが返ってくる。
  // 1 を渡すと {} を返す。
  std::vector<ll> prime_factorize(ll n) {
    assert(L <= n && n < R);
    std::vector<ll> res = large[n - L];
    n /= aux[n - L];
    if (n >= M) {
      // この場合，n は素数となることが示せる（はず）
      // n*n >= R だとオーバーフローしそう？
      res.push_back(n);
      return res;
    }
    while (n > 1) {
      res.push_back(small[n]);
      n /= small[n];
    }
    return res;
  }

  // 約数列挙ライブラリ（ llpow() を含めること！）
  // 非ソート約数列挙 O(logN)+O(dN) ※第二引数を渡さない場合
  // ソート約数列挙 O(logN)+O(dN)+O(dNlogdN) ※第二引数を true に
  // ※ dN = d(N) = σ0(N) = 自然数Nの約数の個数

  // ソート約数列挙をする際は約数の個数に注意
  // その範囲の高度合成数を見れば、約数の個数のmaxが大体分かる
  // （高度合成数一覧）http://wwwhomes.uni-bielefeld.de/achim/highly.txt
  // N<=10^9のとき、log(N)=約30, 約数の個数の最大は735,134,400の1,344個
  // --> ソート約数列挙 : 30+1344+1344*30= 41,694回
  // N<=10^8 のとき O(27)+O(768)+O(768*27)= 21,531回
  // N<=10^7のときd(N)最大は448, N<=10^6のときd(N)最大は240
  // 結論：ソート約数列挙はlong
  // longの範囲内なら全部いけそうだが、1~Nについて行うのは10^5でギリギリか
  std::vector<ll> divisors(ll n, bool sorted = false) {
    assert(L <= n && n < R);
    std::vector<ll> pfactors = large[n - L];
    n /= aux[n - L];
    bool n_is_prime = false;
    if (n >= M) {
      // この場合，n は素数となることが示せる（はず）
      // n*n >= R だとオーバーフローしそう？
      pfactors.push_back(n);
      n_is_prime = true;
    }
    if (!n_is_prime) {
      while (n > 1) {
        pfactors.push_back(small[n]);
        n /= small[n];
      }
    }

    map<ll, ll> map_pfactors;
    for (ll pfac : pfactors) {
      map_pfactors[pfac]++;
    }

    // 約数列挙
    std::vector<ll> res;
    ll len = map_pfactors.size(); // 素因数の種類数
    // 再帰関数で約数を列挙
    make_divisors_from_pfacs(map_pfactors, res, 1, 0);

    // 第二引数が true だった場合、 res をソート
    if (sorted)
      sort(res.begin(), res.end());

    return res;
  }

  // <素因数, 個数> のmapを受け取り、vector resに約数を入れる再帰関数
  // depth := 今見るべき素因数が何個目か(0-indexed) -> map_pfactors[depth]
  void make_divisors_from_pfacs(map<ll, ll> &map_pfactors, vector<ll> &divs_vec,
                                ll nowres, ll depth) {
    auto itr = next(map_pfactors.begin(), depth);
    if (itr == map_pfactors.end()) {
      // 全ての素因数を処理し終わったので、約数をvectorに入れて終了
      // 素因数が1つも無い場合（n=1の場合）、最初からこれになるので、1のみが約数として返される
      divs_vec.push_back(nowres);
      return;
    }
    ll nowpfac = (*itr).first;
    ll nowpfacnum = (*itr).second;
    ll multiplier = 1;
    rep(i, nowpfacnum + 1) {
      ll tmpres = nowres * multiplier;
      make_divisors_from_pfacs(map_pfactors, divs_vec, tmpres, depth + 1);
      multiplier *= nowpfac;
    }
  }
};

// smart_sieve(L, R) で [L, R) を前計算
ll L = 2;
ll R = 1000001;
smart_sieve ss(L, R);

// --------- library end - //

int main() {

  ll N = il();
  vector<ll> as;
  rep(i, N) { as.push_back(il()); }

  // lcmsum_pfacs :=
  // a1~anの最小公倍数を求めるため、a1~anまでを素因数分解し、各素因数の指数をchmaxしていったものを格納するmap
  map<ll, ll> lcmsum_pfacs;

  // 各aiの素因数分解を格納する
  vector<vector<ll>> as_pfacs(N, vector<ll>());

  // 素因数の指数をmapにして、lcmsum_pfacsのchmaxをしていく
  rep(i, N) {
    as_pfacs[i] = ss.prime_factorize(as[i]);

    map<ll, ll> pfac_cnt;
    for (ll ai_pfac : as_pfacs[i]) {
      pfac_cnt[ai_pfac]++;
    }

    for (pll eachpfac : pfac_cnt) {
      chmax(lcmsum_pfacs[eachpfac.first], eachpfac.second);
    }
  }

  // lcmsum を求める
  // lcmsum := a1~anの最小公倍数 mod P
  mint lcmsum = 1;
  for (pll eachpfac : lcmsum_pfacs) {
    mint pfac = eachpfac.first;
    ll cnt = eachpfac.second;
    rep(i, cnt) lcmsum *= pfac;
  }

  // ここから先はmodintで処理する

  // 答えの計算
  // Σ(i:1~N)lcmsum/ai
  mint ans = 0;
  rep(i, N) ans += lcmsum / mint(as[i]);

  cout << ans << endk;
}

Fixed submission:
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
using ld = long double;
using pll = pair<ll, ll>;
using pld = pair<ld, ld>;
const int INF = 1e9 + 7;
const ll LINF = 9223372036854775807;
const ll MOD = 1e9 + 7;
const ld PI = acos(-1);
const ld EPS = 1e-10; // 微調整用（EPSより小さいと0と判定など）

int ii() {
  int x;
  if (scanf("%d", &x) == 1)
    return x;
  else
    return 0;
}
long long il() {
  long long x;
  if (scanf("%lld", &x) == 1)
    return x;
  else
    return 0;
}
string is() {
  string x;
  cin >> x;
  return x;
}
char ic() {
  char x;
  cin >> x;
  return x;
}
void oi(int x) { printf("%d ", x); }
void ol(long long x) { printf("%lld ", x); }
void od_nosp(double x) { printf("%.15f", x); } // 古い問題用
void od(double x) { printf("%.15f ", x); }
// long doubleで受け取り、fをLfなどに変えて出力すると、変な数値が出る
// それをなんとかするには独自の出力を作らなければならなそう
void os(const string &s) { printf("%s ", s.c_str()); }
void oc(const char &c) { printf("%c ", c); }
#define o_map(v)                                                               \
  {                                                                            \
    cerr << #v << endl;                                                        \
    for (const auto &xxx : v) {                                                \
      cout << xxx.first << " " << xxx.second << "\n";                          \
    }                                                                          \
  } // 動作未確認
void br() { putchar('\n'); }

// #define gcd __gcd //llは受け取らない C++17~のgcdと違うので注意
// int lcm(int a, int b){return a / gcd(a, b) * b;}
#define begin_end(a) a.begin(), a.end() // sort(begin_end(vec));
#define REP(i, m, n) for (ll i = (ll)(m); i < (ll)(n); i++)
#define DREP(i, m, n) for (ll i = (ll)(m); i > (ll)(n); i--)
#define rep(i, n) REP(i, 0, n)
#define m_p(a, b) make_pair(a, b)
#define SORT_UNIQUE(c)                                                         \
  (sort(c.begin(), c.end()),                                                   \
   c.resize(distance(c.begin(), unique(c.begin(), c.end()))))
#define p_b push_back
#define SZ(x) ((ll)(x).size()) // size()がunsignedなのでエラー避けに
#define endk '\n'

// coutによるpairの出力（空白区切り）
template <typename T1, typename T2>
ostream &operator<<(ostream &s, const pair<T1, T2> &p) {
  return s << "(" << p.first << " " << p.second << ")";
}
// coutによるvectorの出力（空白区切り）
template <typename T> ostream &operator<<(ostream &s, const vector<T> &v) {
  int len = v.size();
  for (int i = 0; i < len; ++i) {
    s << v[i];
    if (i < len - 1)
      s << " "; //"\t"に変えるとTabで見やすく区切る
  }
  return s;
}
// coutによる多次元vectorの出力（空白区切り）
template <typename T>
ostream &operator<<(ostream &s, const vector<vector<T>> &vv) {
  int len = vv.size();
  for (int i = 0; i < len; ++i) {
    s << vv[i] << endl;
  }
  return s;
}

// 最大値、最小値の更新。更新したor等しければtrueを返す
template <typename T> bool chmax(T &a, T b) { return (a = max(a, b)) == b; }
template <typename T> bool chmin(T &a, T b) { return (a = min(a, b)) == b; }

// 4近傍（上下左右） rep(i, 2) にすると右・下だけに進む
vector<int> dx_4 = {1, 0, -1, 0};
vector<int> dy_4 = {0, 1, 0, -1};

// -------- template end - //

// - library ------------- //

// modint: mod 計算を int を扱うように扱える構造体
template <int MOD> struct Fp {
  long long val;
  constexpr Fp(long long v = 0) noexcept : val(v % MOD) {
    if (val < 0)
      val += MOD;
  }
  constexpr int getmod() { return MOD; }
  constexpr Fp operator-() const noexcept { return val ? MOD - val : 0; }
  constexpr Fp operator+(const Fp &r) const noexcept { return Fp(*this) += r; }
  constexpr Fp operator-(const Fp &r) const noexcept { return Fp(*this) -= r; }
  constexpr Fp operator*(const Fp &r) const noexcept { return Fp(*this) *= r; }
  constexpr Fp operator/(const Fp &r) const noexcept { return Fp(*this) /= r; }
  constexpr Fp &operator+=(const Fp &r) noexcept {
    val += r.val;
    if (val >= MOD)
      val -= MOD;
    return *this;
  }
  constexpr Fp &operator-=(const Fp &r) noexcept {
    val -= r.val;
    if (val < 0)
      val += MOD;
    return *this;
  }
  constexpr Fp &operator*=(const Fp &r) noexcept {
    val = val * r.val % MOD;
    return *this;
  }
  constexpr Fp &operator/=(const Fp &r) noexcept {
    long long a = r.val, b = MOD, u = 1, v = 0;
    while (b) {
      long long t = a / b;
      a -= t * b;
      swap(a, b);
      u -= t * v;
      swap(u, v);
    }
    val = val * u % MOD;
    if (val < 0)
      val += MOD;
    return *this;
  }
  constexpr bool operator==(const Fp &r) const noexcept {
    return this->val == r.val;
  }
  constexpr bool operator!=(const Fp &r) const noexcept {
    return this->val != r.val;
  }
  friend constexpr ostream &operator<<(ostream &os, const Fp<MOD> &x) noexcept {
    return os << x.val;
  }
  friend constexpr Fp<MOD> modpow(const Fp<MOD> &a, long long n) noexcept {
    if (n == 0)
      return 1;
    auto t = modpow(a, n / 2);
    t = t * t;
    if (n & 1)
      t = t * a;
    return t;
  }
};

using mint = Fp<MOD>;

// エラトステネスの篩に基づく高速な素因数分解
// 前処理したあと O(logN)でNを素因数分解
// 以下のページのライブラリを改造したもの
// https://qiita.com/rsk0315_h4x/items/ff3b542a4468679fb409
class smart_sieve {
  ll L, R, M;
  std::vector<int> small;             // 小さい篩
  std::vector<std::vector<ll>> large; // 大きい篩
  std::vector<ll> aux;                // aux[i] := large[i] の素因数の積

public:
  smart_sieve(ll L, ll R) : L(L), R(R), M(sqrt(R) + 1) {
    small.resize(M);
    std::iota(small.begin(), small.end(), 0);
    large.resize(R - L);
    aux.assign(R - L, 1);

    for (ll i = 2; i * i < R; ++i) {
      if (small[i] < i)
        continue;
      small[i] = i;
      for (ll j = i * i; j < M; j += i)
        if (small[j] == j)
          small[j] = i;

      for (ll j = (L + i - 1) / i * i; j < R; j += i) {
        ll k = j;
        do {
          // aux[j-L] > M で判定した方がいいかも？
          // j / aux[j-L] < M の方がいい？（割り算したくない）
          if (aux[j - L] * aux[j - L] > R)
            break;

          large[j - L].push_back(i);
          aux[j - L] *= i;
          k /= i;
        } while (k % i == 0);
      }
    }
  }

  // 素因数分解 O(logN)
  // factor(i) : i の素因数vector : f(120) = 2 2 2 3 5（ソート済）
  // 0 を渡すと おかしい答えが返ってくる。
  // 1 を渡すと {} を返す。
  std::vector<ll> prime_factorize(ll n) {
    assert(L <= n && n < R);
    std::vector<ll> res = large[n - L];
    n /= aux[n - L];
    if (n >= M) {
      // この場合，n は素数となることが示せる（はず）
      // n*n >= R だとオーバーフローしそう？
      res.push_back(n);
      return res;
    }
    while (n > 1) {
      res.push_back(small[n]);
      n /= small[n];
    }
    return res;
  }

  // 約数列挙ライブラリ（ llpow() を含めること！）
  // 非ソート約数列挙 O(logN)+O(dN) ※第二引数を渡さない場合
  // ソート約数列挙 O(logN)+O(dN)+O(dNlogdN) ※第二引数を true に
  // ※ dN = d(N) = σ0(N) = 自然数Nの約数の個数

  // ソート約数列挙をする際は約数の個数に注意
  // その範囲の高度合成数を見れば、約数の個数のmaxが大体分かる
  // （高度合成数一覧）http://wwwhomes.uni-bielefeld.de/achim/highly.txt
  // N<=10^9のとき、log(N)=約30, 約数の個数の最大は735,134,400の1,344個
  // --> ソート約数列挙 : 30+1344+1344*30= 41,694回
  // N<=10^8 のとき O(27)+O(768)+O(768*27)= 21,531回
  // N<=10^7のときd(N)最大は448, N<=10^6のときd(N)最大は240
  // 結論：ソート約数列挙はlong
  // longの範囲内なら全部いけそうだが、1~Nについて行うのは10^5でギリギリか
  std::vector<ll> divisors(ll n, bool sorted = false) {
    assert(L <= n && n < R);
    std::vector<ll> pfactors = large[n - L];
    n /= aux[n - L];
    bool n_is_prime = false;
    if (n >= M) {
      // この場合，n は素数となることが示せる（はず）
      // n*n >= R だとオーバーフローしそう？
      pfactors.push_back(n);
      n_is_prime = true;
    }
    if (!n_is_prime) {
      while (n > 1) {
        pfactors.push_back(small[n]);
        n /= small[n];
      }
    }

    map<ll, ll> map_pfactors;
    for (ll pfac : pfactors) {
      map_pfactors[pfac]++;
    }

    // 約数列挙
    std::vector<ll> res;
    ll len = map_pfactors.size(); // 素因数の種類数
    // 再帰関数で約数を列挙
    make_divisors_from_pfacs(map_pfactors, res, 1, 0);

    // 第二引数が true だった場合、 res をソート
    if (sorted)
      sort(res.begin(), res.end());

    return res;
  }

  // <素因数, 個数> のmapを受け取り、vector resに約数を入れる再帰関数
  // depth := 今見るべき素因数が何個目か(0-indexed) -> map_pfactors[depth]
  void make_divisors_from_pfacs(map<ll, ll> &map_pfactors, vector<ll> &divs_vec,
                                ll nowres, ll depth) {
    auto itr = next(map_pfactors.begin(), depth);
    if (itr == map_pfactors.end()) {
      // 全ての素因数を処理し終わったので、約数をvectorに入れて終了
      // 素因数が1つも無い場合（n=1の場合）、最初からこれになるので、1のみが約数として返される
      divs_vec.push_back(nowres);
      return;
    }
    ll nowpfac = (*itr).first;
    ll nowpfacnum = (*itr).second;
    ll multiplier = 1;
    rep(i, nowpfacnum + 1) {
      ll tmpres = nowres * multiplier;
      make_divisors_from_pfacs(map_pfactors, divs_vec, tmpres, depth + 1);
      multiplier *= nowpfac;
    }
  }
};

// smart_sieve(L, R) で [L, R) を前計算
ll L = 1;
ll R = 1000001;
smart_sieve ss(L, R);

// --------- library end - //

int main() {

  ll N = il();
  vector<ll> as;
  rep(i, N) { as.push_back(il()); }

  // lcmsum_pfacs :=
  // a1~anの最小公倍数を求めるため、a1~anまでを素因数分解し、各素因数の指数をchmaxしていったものを格納するmap
  map<ll, ll> lcmsum_pfacs;

  // 各aiの素因数分解を格納する
  vector<vector<ll>> as_pfacs(N, vector<ll>());

  // 素因数の指数をmapにして、lcmsum_pfacsのchmaxをしていく
  rep(i, N) {
    as_pfacs[i] = ss.prime_factorize(as[i]);

    map<ll, ll> pfac_cnt;
    for (ll ai_pfac : as_pfacs[i]) {
      pfac_cnt[ai_pfac]++;
    }

    for (pll eachpfac : pfac_cnt) {
      chmax(lcmsum_pfacs[eachpfac.first], eachpfac.second);
    }
  }

  // lcmsum を求める
  // lcmsum := a1~anの最小公倍数 mod P
  mint lcmsum = 1;
  for (pll eachpfac : lcmsum_pfacs) {
    mint pfac = eachpfac.first;
    ll cnt = eachpfac.second;
    rep(i, cnt) lcmsum *= pfac;
  }

  // ここから先はmodintで処理する

  // 答えの計算
  // Σ(i:1~N)lcmsum/ai
  mint ans = 0;
  rep(i, N) ans += lcmsum / mint(as[i]);

  cout << ans << endk;
}