Problem p00099 (C++) — buggy submission, verdict: Runtime Error

// RMQ問題：次の２つのクエリがたくさん与えられる,高速に答えよ。
// 1.区間[i,j]内の要素ai〜ajの最大値をこたえる。2.要素aiにxを追加する。
// ただし、0 <= i,j <= Nとする。最初はa0〜ajは全てINF(= -1e+8)である。

// 単純に配列でシュミレーションすると、常にO(NQ),累積和を使うと、クエリ1はO(1),クエリ2はO(N)かかるので、最悪O(NQ)
// 領域を2分割,2分割していって、塊で答えを覚えておけばクエリ1は木の深さ分でできる。
// 葉ノードの区間長が1ならば、葉ノードを変える→根まで木の深さ分更新(O(logN))でできる。
// ということで、セグメントツリー(初実装)で解く.(蛇足 :
// 大きい区間の答えを持つ,ノードが便利なのはもちろん、葉ノードは1要素になってるから、区間長1の変更も簡単。

// RMQのペア(番号,数)バージョン。2この変数でソートされた平衡２分木でもできると思う。

#include <algorithm>
#include <cmath>
#include <iostream>
#include <vector>
#define rep(i, n) for (i = 0; i < n; i++)

using namespace std;

typedef pair<int, int> P;

// 完全２分木にしよう。(要素数によっては右側の要素を使わないかも…)
class segtree {
  P *a; // 配列で表現。根 = 0,左の子のノード番号 = 親ノード番号*2 +
        // 1。なので、区間長についてはあまり意識しなくてよい。
  int dep; // 根から葉までの距離

public:
  segtree(int depth, int x) {
    int i;
    dep = depth;
    a = new P[1 << (dep + 1)];
    rep(i, 1 << (dep + 1)) a[i] = P(1, x);
  }
  ~segtree() { delete[] a; }
  void insert(int i, int x) // 根の左からi(>=0)番目にxを加算する
  {
    int num = i + (1 << dep);
    int E; // 左のノード(ノード番号が奇数)なら1,右のノードなら-1

    // 値の追加
    a[num] = P(i, a[num].second + x);

    // ツリーの更新
    while (num > 0) {
      E = (num % 2) ? 1 : -1;
      if (a[num].second > a[num + E].second ||
          (a[num].second == a[num + E].second &&
           a[num].first < a[num + E].first))
        a[(num - 1) >> 1] = a[num];
      else
        a[(num - 1) >> 1] = a[num + E];
      num = (num - 1) >> 1;
    }
  }
  P getmax(int i, int j) {
    P ans = a[i + (1 << dep)];

    if (i == j)
      return ans;
    if (i > j)
      swap(i, j);
    // 遅延処理.
    // 葉から見ていく方法でやろう。a[num_i]とa[num_j]を見ている。num_i <
    // num_j.みたいな 閉区間
    int num_i = i + (1 << dep); // 左の子ならその親、右の子ならその親+1に移動
    int num_j = j + (1 << dep); // 左の子ならその親-1、右の子ならその親に移動

    while (num_i <= num_j) {
      if ((ans.second < a[num_i].second) ||
          (ans.second == a[num_i].second && ans.first > a[num_i].first))
        ans = a[num_i];
      if ((ans.second < a[num_j].second) ||
          (ans.second == a[num_j].second && ans.first > a[num_j].first))
        ans = a[num_j];
      num_i = (num_i) % 2 ? ((num_i - 1) >> 1) : (((num_i - 1) >> 1) + 1);
      num_j = (num_j) % 2 ? (((num_j - 1) >> 1) - 1) : ((num_j - 1) >> 1);
    }
    return ans;
  }
  void print_a(int n) {
    for (int i = 0; i < n; i++)
      cout << "a[i + (1 << dep)] = " << a[i + (1 << dep)].first << " "
           << a[i + (1 << dep)].second << endl;
  }
};

int main() {
  int i;
  int n, q;
  int a, b;
  P ans[100000];
  segtree seg(21, 0);

  cin >> n >> q;
  for (i = 0; i < q; i++) {
    cin >> a >> b;
    seg.insert(a, b);
    // seg.print_a(n+1);
    ans[i] = seg.getmax(1, n);
  }
  for (i = 0; i < q; i++) {
    cout << ans[i].first << " " << ans[i].second << endl;
  }
  return 0;
}

Error: 0

Fixed submission:

// RMQ問題：次の２つのクエリがたくさん与えられる,高速に答えよ。
// 1.区間[i,j]内の要素ai〜ajの最大値をこたえる。2.要素aiにxを追加する。
// ただし、0 <= i,j <= Nとする。最初はa0〜ajは全てINF(= -1e+8)である。

// 単純に配列でシュミレーションすると、常にO(NQ),累積和を使うと、クエリ1はO(1),クエリ2はO(N)かかるので、最悪O(NQ)
// 領域を2分割,2分割していって、塊で答えを覚えておけばクエリ1は木の深さ分でできる。
// 葉ノードの区間長が1ならば、葉ノードを変える→根まで木の深さ分更新(O(logN))でできる。
// ということで、セグメントツリー(初実装)で解く.(蛇足 :
// 大きい区間の答えを持つ,ノードが便利なのはもちろん、葉ノードは1要素になってるから、区間長1の変更も簡単。

// RMQのペア(番号,数)バージョン。2この変数でソートされた平衡２分木でもできると思う。

#include <algorithm>
#include <cmath>
#include <iostream>
#include <vector>
#define rep(i, n) for (i = 0; i < n; i++)

using namespace std;

typedef pair<int, int> P;

// 完全２分木にしよう。(要素数によっては右側の要素を使わないかも…)
class segtree {
  P *a; // 配列で表現。根 = 0,左の子のノード番号 = 親ノード番号*2 +
        // 1。なので、区間長についてはあまり意識しなくてよい。
  int dep; // 根から葉までの距離

public:
  segtree(int depth, int x) {
    int i;
    dep = depth;
    a = new P[1 << (dep + 1)];
    rep(i, 1 << (dep + 1)) a[i] = P(1, x);
  }
  ~segtree() { delete[] a; }
  void insert(int i, int x) // 根の左からi(>=0)番目にxを加算する
  {
    int num = i + (1 << dep);
    int E; // 左のノード(ノード番号が奇数)なら1,右のノードなら-1

    // 値の追加
    a[num] = P(i, a[num].second + x);

    // ツリーの更新
    while (num > 0) {
      E = (num % 2) ? 1 : -1;
      if (a[num].second > a[num + E].second ||
          (a[num].second == a[num + E].second &&
           a[num].first < a[num + E].first))
        a[(num - 1) >> 1] = a[num];
      else
        a[(num - 1) >> 1] = a[num + E];
      num = (num - 1) >> 1;
    }
  }
  P getmax(int i, int j) {
    P ans = a[i + (1 << dep)];

    if (i == j)
      return ans;
    if (i > j)
      swap(i, j);
    // 遅延処理.
    // 葉から見ていく方法でやろう。a[num_i]とa[num_j]を見ている。num_i <
    // num_j.みたいな 閉区間
    int num_i = i + (1 << dep); // 左の子ならその親、右の子ならその親+1に移動
    int num_j = j + (1 << dep); // 左の子ならその親-1、右の子ならその親に移動

    while (num_i <= num_j) {
      if ((ans.second < a[num_i].second) ||
          (ans.second == a[num_i].second && ans.first > a[num_i].first))
        ans = a[num_i];
      if ((ans.second < a[num_j].second) ||
          (ans.second == a[num_j].second && ans.first > a[num_j].first))
        ans = a[num_j];
      num_i = (num_i) % 2 ? ((num_i - 1) >> 1) : (((num_i - 1) >> 1) + 1);
      num_j = (num_j) % 2 ? (((num_j - 1) >> 1) - 1) : ((num_j - 1) >> 1);
    }
    return ans;
  }
  void print_a(int n) {
    for (int i = 0; i < n; i++)
      cout << "a[i + (1 << dep)] = " << a[i + (1 << dep)].first << " "
           << a[i + (1 << dep)].second << endl;
  }
};

int main() {
  int i;
  int n, q;
  int a, b;
  P ans[50000];
  segtree seg(18, 0);

  cin >> n >> q;
  for (i = 0; i < q; i++) {
    cin >> a >> b;
    seg.insert(a, b);
    // seg.print_a(n+1);
    ans[i] = seg.getmax(1, n);
  }
  for (i = 0; i < q; i++) {
    cout << ans[i].first << " " << ans[i].second << endl;
  }
  return 0;
}