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

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define int ll

template <typename T1, typename T2> inline void chmin(T1 &a, T2 b) {
  if (a > b)
    a = b;
}
template <typename T1, typename T2> inline void chmax(T1 &a, T2 b) {
  if (a < b)
    a = b;
}

#define REP(i, n) for (int i = 0; i < n; ++i)
#define SORT(name) sort(name.begin(), name.end())
#define ZERO(p) memset(p, 0, sizeof(p))
#define MINUS(p) memset(p, -1, sizeof(p))
#if 1
#define DBG(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define DBG(fmt, ...)
#endif

const ll LLINF = (1LL << 60);
const int INF = (1LL << 30);
const double DINF = std::numeric_limits<double>::infinity();
const int MOD = 1000000007;
const int MAX_N = 100010;

template <typename T> class CumulativeSum {
public:
  CumulativeSum() {}
  CumulativeSum(int sz) : m_Data(sz, T(0)){};

  void Init(int sz) { m_Data.assign(sz, T(0)); }

  // k に x を加える O(1)
  void Add(int k, T x) {
    assert(static_cast<int>(m_Data.size()) >= k);
    if (static_cast<int>(m_Data.size()) <= k) {
      m_Data.push_back(x);
    } else {
      m_Data[k] = (m_Data[k] + x);
    }
  }

  // 累積和の構築 O(m_Data.size())
  void Build() {
    for (int i = 1; i < static_cast<int>(m_Data.size()); i++) {
      m_Data[i] = (m_Data[i] + m_Data[i - 1]);
    }
  }

  // 区間 [0, k] の和を返す O(1)
  // 閉区間なので k が含まれることに注意
  T Query(int k) {
    if (k < 0) {
      return T(0);
    }
    return (m_Data[min(k, static_cast<int>(m_Data.size()) - 1)]);
  }

  // 区間 [l, h] の和を返す O(1)
  // 閉区間なので k が含まれることに注意
  T Query(int l, int h) { return (this->Query(h) - this->Query(l - 1)); }

private:
  vector<T> m_Data;
};

signed main() {
  cin.tie(0);
  cout.tie(0);
  ios::sync_with_stdio(false);

  string S;
  cin >> S;

  reverse(S.begin(), S.end());

  // [0, i] で表される数を 2019 で割ったあまり
  vector<int> mod(S.length());
  mod[0] = S[0] - '0';
  int mul = 10;
  for (int i = 1; i < S.length(); ++i) {
    int num = S[i] - '0';
    num = (num * mul) % 2019;
    mod[i] = (mod[i - 1] + num) % 2019;
    mul *= 10;
    mul %= 2019;
  }

  // 数字 i の出現数
  vector<int> cnt(2100, 0);

  int ans = 0;
  for (int i = 0; i < S.length(); ++i) {
    ans += cnt[mod[i]];
    if (mod[i] == 0) {
      ans++;
    }
    cnt[mod[i]]++;
  }
  printf("%lld\\n", ans);

  return 0;
}


Error: TLE

Fixed submission:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define int ll

template <typename T1, typename T2> inline void chmin(T1 &a, T2 b) {
  if (a > b)
    a = b;
}
template <typename T1, typename T2> inline void chmax(T1 &a, T2 b) {
  if (a < b)
    a = b;
}

#define REP(i, n) for (int i = 0; i < n; ++i)
#define SORT(name) sort(name.begin(), name.end())
#define ZERO(p) memset(p, 0, sizeof(p))
#define MINUS(p) memset(p, -1, sizeof(p))
#if 1
#define DBG(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define DBG(fmt, ...)
#endif

const ll LLINF = (1LL << 60);
const int INF = (1LL << 30);
const double DINF = std::numeric_limits<double>::infinity();
const int MOD = 1000000007;
const int MAX_N = 100010;

template <typename T> class CumulativeSum {
public:
  CumulativeSum() {}
  CumulativeSum(int sz) : m_Data(sz, T(0)){};

  void Init(int sz) { m_Data.assign(sz, T(0)); }

  // k に x を加える O(1)
  void Add(int k, T x) {
    assert(static_cast<int>(m_Data.size()) >= k);
    if (static_cast<int>(m_Data.size()) <= k) {
      m_Data.push_back(x);
    } else {
      m_Data[k] = (m_Data[k] + x);
    }
  }

  // 累積和の構築 O(m_Data.size())
  void Build() {
    for (int i = 1; i < static_cast<int>(m_Data.size()); i++) {
      m_Data[i] = (m_Data[i] + m_Data[i - 1]);
    }
  }

  // 区間 [0, k] の和を返す O(1)
  // 閉区間なので k が含まれることに注意
  T Query(int k) {
    if (k < 0) {
      return T(0);
    }
    return (m_Data[min(k, static_cast<int>(m_Data.size()) - 1)]);
  }

  // 区間 [l, h] の和を返す O(1)
  // 閉区間なので k が含まれることに注意
  T Query(int l, int h) { return (this->Query(h) - this->Query(l - 1)); }

private:
  vector<T> m_Data;
};

signed main() {
  cin.tie(0);
  cout.tie(0);
  ios::sync_with_stdio(false);

  string S;
  cin >> S;

  reverse(S.begin(), S.end());

  // [0, i] で表される数を 2019 で割ったあまり
  vector<int> mod(S.length());
  mod[0] = S[0] - '0';
  int mul = 10;
  for (int i = 1; i < S.length(); ++i) {
    int num = S[i] - '0';
    num = (num * mul) % 2019;
    mod[i] = (mod[i - 1] + num) % 2019;
    mul *= 10;
    mul %= 2019;
  }

  // 数字 i のある数
  vector<CumulativeSum<int>> cnt(2100, CumulativeSum<int>(S.length()));
  REP(i, S.length()) { cnt[mod[i]].Add(i, 1); }
  REP(i, 2100) { cnt[i].Build(); }

  int ans = 0;
  for (int i = 1; i < S.length(); ++i) {
    ans += cnt[mod[i]].Query(i - 1);
    if (mod[i] == 0) {
      ans++;
    }
  }
  printf("%lld\\n", ans);

  return 0;
}