Problem p02624 (C++) — buggy submission, verdict: Time Limit Exceeded

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

using ll = long long;
using ld = long double;
using pint = pair<int, int>;
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 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 GET_POS(c, x) (lower_bound(c.begin(), c.end(), x) - c.begin())
#define p_b push_back
#define SZ(x) ((int)(x).size) // size()がunsignedなのでエラー避けに

// 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 ------------- //

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

int main() {

  ll N = il();

  vector<ll> divisor_cnts(10000001, 2);
  divisor_cnts[1] = 1;
  // 1~10^7は全て1の倍数であり、2以上は全てその数自身の倍数でもあるので基本は初期値が2、1の約数だけ1個

  REP(i, 2, 5000001) {
    // 10^7/2 までについて、その数の倍数に+1していく（これで約数の個数が入る）
    ll tmpi = i * 2; // i自身は初期化の時点で入れてあるので、x2から始める
    while (tmpi <= 10000000) {
      divisor_cnts[tmpi]++;
      tmpi += i;
    }
  }

  ll ans = 0;
  REP(i, 1, N + 1) { ans += i * divisor_cnts[i]; }

  cout << ans << endl;
}

Error: TLE

Fixed submission:

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

using ll = long long;
using ld = long double;
using pint = pair<int, int>;
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 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 GET_POS(c, x) (lower_bound(c.begin(), c.end(), x) - c.begin())
#define p_b push_back
#define SZ(x) ((int)(x).size) // size()がunsignedなのでエラー避けに

// 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 ------------- //

// ちゃんとした整数計算で累乗を返す。MODなしへの対応版。
// 10^18まではいけるようだが、10^19でオーバーフローする
// MODをつけたい場合もこれでもいい
// 繰り返し二乗法を使っているので O(logN) で計算できる
ll llpow(ll a, ll b) {
  if (b == 0) {
    return 1;
  }
  ll ans = llpow(a, b / 2);
  ans = ans * ans; // % MOD; (MODつけたい場合のコピー用)
  if (b % 2 == 1) {
    ans = ans * a; // % MOD; (MODつけたい場合のコピー用)
  }
  return ans;
}

// エラトステネスの篩に基づく高速な素因数分解
// 前処理したあと 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;
    }
  }
};

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

int main() {

  ll N = il();

  vector<ll> divisor_cnts(10000001, 0);
  divisor_cnts[1] = 1;

  // smart_sieve(L, R) で [L, R) を前計算
  rep(i, 10) {
    ll L = i * 1000000;
    ll R = (i + 1) * 1000000 + 1;
    if (i == 0) {
      L = 2;
      smart_sieve ss(L, R);
      REP(j, 2, (i + 1) * 1000000 + 1) {
        divisor_cnts[j] = ss.divisors(j).size();
      }
    } else {
      smart_sieve ss(L, R);
      REP(j, i * 1000000, (i + 1) * 1000000 + 1) {
        divisor_cnts[j] = ss.divisors(j).size();
      }
    }
  }

  ll ans = 1;
  REP(i, 2, N + 1) { ans += i * divisor_cnts[i]; }

  cout << ans << endl;
}