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

#include <algorithm>
#include <cassert>    // assert
#include <cmath>      // abs など
#include <functional> // greater など
#include <iostream>
#include <numeric> // accumulate など
#include <queue>
#include <set>
#include <stdexcept>
#include <string>
#include <tuple>
#include <vector>
using namespace std;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
typedef long long ll;

#define NDEBUG 1
// debug
// https://trap.jp/post/998/
void debug_out() { cout << endl; }
template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) {
  clog << H << " ";
  debug_out(T...);
}
#ifdef NDEBUG
#define debug(...)
#else
#define debug(...) debug_out(__VA_ARGS__)
#endif

const ll bigprime = 1000000007;
const int MAX_N = 200009;
int size_ambient_tree;
vector<ll> modfactmemo(1, 1);
/*
struct edge
{
  int from, to;
};
vector<edge> edges;
*/
vector<ll> dp1(MAX_N);             // 1回目の探索での結果
vector<ll> dp2(MAX_N);             // 最終的な結果
const ll identity = 1;             // モノイドの単位元
const int initial_root = 0;        // 1回目の根
vector<vector<ll>> accum_l(MAX_N); // 2回目の探索での累積(左から)
vector<vector<ll>> accum_r(MAX_N); // 2回目の探索での累積(右から)
vector<int> sizes_subtree(
    MAX_N); // initial_root を根としたときの、各ノードを根とする部分木のサイズ
vector<vector<int>> adjs(MAX_N); // 隣接するノードのリスト
vector<int> index_adjs(MAX_N);   // 親の何番目のadjsか？

ll modfact(int n) {
  modfactmemo.reserve(MAX_N); // 一度だけ呼べば良い 何なら無くてもいい
  if (n < 0) {
    throw domain_error("modfact: n must not be negative");
  } else if (n >= modfactmemo.size()) {
    ll res = modfact(n - 1);
    // assert(modfactmemo.size() == n);
    modfactmemo.push_back(res * n % bigprime);
  }
  return modfactmemo.at(n);
}

ll modpow(ll n, ll k) {
  if (k < 0) {
    throw domain_error("modpow: k must not be negative");
  } else if (k == 0) {
    return 1;
  } else if (k % 2 == 0) {
    ll res = modpow(n, k / 2);
    return res * res % bigprime;
  } else {
    ll res = modpow(n, k - 1);
    return res * n % bigprime;
  }
}

inline ll modinv(ll n) {
  ll m = n % bigprime;
  if (m == 0) {
    throw domain_error("modinv: 0 is not invertible");
  }
  return modpow(m, bigprime - 2);
}

// dp2の値から結果を求める
inline ll convert_result(int size_subtree, ll arg) {
  return modfact(size_subtree) * arg % bigprime;
}

// 子すべてでの値をマージした値から、あるnodeでの値を得る
// size_subtreeはnodeを含めて数える
inline ll calc_on_node(int size_subtree, ll merged) {
  return modinv(size_subtree) * merged % bigprime;
}

// モノイド演算
inline ll merge(ll x, ll y) { return x * y % bigprime; }

// 普通の木DPでsize_subtree, dp1, index_adjsを求める
void dfs1(const int node, const int parent) {
  int size_subtree = 1; // node以下の部分木のサイズ
  ll merged = identity; // nodeの子すべてでの値をマージした値

  // 子を順番に処理
  for (int i = 0; i < adjs.at(node).size(); i++) {
    // 親は無視
    const int child = adjs.at(node).at(i);
    if (child == parent) {
      continue;
    }

    dfs1(child, node);

    // 値を更新
    index_adjs.at(child) = i;
    size_subtree += sizes_subtree.at(child);
    merged = merge(merged, dp1.at(child));
  }

  // 値を格納
  sizes_subtree.at(node) = size_subtree;
  dp1.at(node) = calc_on_node(size_subtree, merged);
}

// 全方位木DPでdp2を求める
void dfs2(int node, int parent) {
  ll parent_res; // 全体の木からnode以下の部分木を取り除きparentを根として得られる部分木での値

  // 親がいればparent_resを求める
  // 親がいないなら定義されない
  if (node != initial_root) {
    int index = index_adjs.at(node);
    // node以外のparentの子すべてでの値をマージした値
    ll parent_merged =
        merge(accum_l.at(parent).at(index), accum_r.at(parent).at(index));
    parent_res =
        calc_on_node(size_ambient_tree - sizes_subtree.at(node), parent_merged);
  }

  // accum_l, accum_r, dp2 を求める
  const int num_adjs = adjs.at(node).size();
  accum_l.at(node).resize(num_adjs +
                          1); // すべてマージした値も求めておくため+1してある
  accum_l.at(node).at(0) = identity;
  accum_r.at(node).resize(num_adjs);
  accum_r.at(node).at(num_adjs - 1) = identity;
  for (int i = 1; i <= num_adjs; i++) {
    int adj = adjs.at(node).at(i - 1);
    ll res = (adj == parent) ? parent_res : dp1.at(adj);
    accum_l.at(node).at(i) = merge(accum_l.at(node).at(i - 1), res);
  }
  for (int i = num_adjs - 2; i >= 0; i--) {
    int adj = adjs.at(node).at(i + 1);
    ll res = (adj == parent) ? parent_res : dp1.at(adj);
    accum_r.at(node).at(i) = merge(accum_r.at(node).at(i + 1), res);
  }
  dp2.at(node) = calc_on_node(size_ambient_tree, accum_l.at(node).at(num_adjs));

  // 子についても処理する
  for (const int child : adjs.at(node)) {
    if (child == parent) {
      continue;
    }
    dfs2(child, node);
  }
}

int main() {
  // 入力を取る
  cin >> size_ambient_tree;
  rep(i, size_ambient_tree - 1) {
    int a, b;
    cin >> a >> b;
    a--;
    b--; // 入力では1から始まるのでデクリメントする必要がある!
    // edges.push_back(edge{a, b});
    adjs.at(a).push_back(b);
    adjs.at(b).push_back(a);
  }

  // 処理
  // index_adjs.at(initial_root) = -1;
  dfs1(initial_root, -1);
  dfs2(initial_root, -1);

  // 出力
  rep(i, size_ambient_tree) {
    cout << convert_result(size_ambient_tree, dp2.at(i)) << endl;
  }
}


Error: -6
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 1) >= this->size() (which is 1)


Fixed submission:

#include <algorithm>
#include <cassert>
#include <cmath>      // abs など
#include <functional> // greater など
#include <iostream>
#include <numeric> // accumulate など
#include <queue>
#include <set>
#include <stdexcept>
#include <string>
#include <tuple>
#include <vector>
using namespace std;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
typedef long long ll;

const ll bigprime = 1000000007;
vector<ll> modfactmemo(1, 1); // modfactのメモ化に使う配列

const int MAX_N = 200009;
int size_ambient_tree;
/*
struct edge
{
  int from, to;
};
vector<edge> edges;
*/
vector<ll> dp1(MAX_N);             // 1回目の探索での結果
vector<ll> dp2(MAX_N);             // 最終的な結果
const ll identity = 1;             // モノイドの単位元
const int initial_root = 0;        // 1回目の根
vector<vector<ll>> accum_l(MAX_N); // 2回目の探索での累積(左から)
vector<vector<ll>> accum_r(MAX_N); // 2回目の探索での累積(右から)
vector<int> sizes_subtree(
    MAX_N); // initial_root を根としたときの、各ノードを根とする部分木のサイズ
vector<vector<int>> adjs(MAX_N, vector<int>()); // 隣接するノードのリスト
vector<int> index_adjs(MAX_N);                  // 親の何番目のadjsか？

ll modfact(int n) {
  if (n < 0) {
    throw domain_error("modfact: n must not be negative");
  } else if (n >= modfactmemo.size()) {
    ll res = modfact(n - 1) * n % bigprime;
    modfactmemo.at(n) = res;
  }
  return modfactmemo.at(n);
}

ll modpow(ll n, ll k) {
  if (k == 0) {
    return 1;
  } else if (k < 0) {
    throw domain_error("modpow: k must not be negative");
  } else if (k % 2 == 0) {
    ll res = modpow(n, k / 2);
    return res * res % bigprime;
  } else {
    ll res = modpow(n, k - 1);
    return res * n % bigprime;
  }
}

inline ll modinv(ll n) {
  ll m = n % bigprime;
  if (m == 0) {
    throw domain_error("modinv: 0 is not invertible");
  }
  return modpow(m, bigprime - 2);
}

// dp2の値から結果を求める
inline ll convert_result(int size_subtree, ll arg) {
  return modfact(size_subtree) * arg % bigprime;
}

// 子すべてでの値をマージした値から、あるnodeでの値を得る
// size_subtreeはnodeを含めて数える
inline ll calc_on_node(int size_subtree, ll merged) {
  return modinv(size_subtree) * merged % bigprime;
}

// モノイド演算
inline ll merge(ll x, ll y) { return x * y % bigprime; }

// 普通の木DPでsize_subtree, dp1, index_adjsを求める
void dfs1(int node, int parent) {
  int size_subtree = 1; // node以下の部分木のサイズ
  ll merged = identity; // nodeの子すべてでの値をマージした値

  // 子を順番に処理
  for (int i = 0; i < adjs.at(node).size(); i++) {
    // 親は無視
    int child = adjs.at(node).at(i);
    if (child == parent) {
      continue;
    }

    dfs1(child, node);

    // 値を更新
    index_adjs.at(child) = i;
    size_subtree += sizes_subtree.at(child);
    merged = merge(merged, dp1.at(child));
  }

  // 値を格納
  sizes_subtree.at(node) = size_subtree;
  dp1.at(node) = calc_on_node(size_subtree, merged);
}

// 全方位木DPでdp2を求める
void dfs2(int node, int parent) {
  ll parent_res; // 全体の木からnode以下の部分木を取り除きparentを根として得られる部分木での値

  // 親がいればparent_resを求める
  // 親がいないなら定義されない
  if (node != initial_root) {
    int index = index_adjs.at(node);
    // node以外のparentの子すべてでの値をマージした値
    ll parent_merged =
        merge(accum_l.at(parent).at(index), accum_r.at(parent).at(index));
    parent_res =
        calc_on_node(size_ambient_tree - sizes_subtree.at(node), parent_merged);
  }

  // accum_l, accum_r, dp2 を求める
  int num_adjs = adjs.at(node).size();
  accum_l.at(node).resize(num_adjs +
                          1); // すべてマージした値も求めておくため+1してある
  accum_l.at(node).at(0) = identity;
  accum_r.at(node).resize(num_adjs);
  accum_r.at(node).at(num_adjs - 1) = identity;
  for (int i = 1; i <= num_adjs; i++) {
    int adj = adjs.at(node).at(i - 1);
    ll res = (adj == parent) ? parent_res : dp1.at(adj);
    accum_l.at(node).at(i) = merge(accum_l.at(node).at(i - 1), res);
  }
  for (int i = num_adjs - 2; i >= 0; i--) {
    int adj = adjs.at(node).at(i + 1);
    ll res = (adj == parent) ? parent_res : dp1.at(adj);
    accum_r.at(node).at(i) = merge(accum_r.at(node).at(i + 1), res);
  }
  dp2.at(node) = calc_on_node(size_ambient_tree, accum_l.at(node).at(num_adjs));

  // 子についても処理する
  for (int child : adjs.at(node)) {
    if (child == parent) {
      continue;
    }
    dfs2(child, node);
  }
}

int main() {
  // 入力を取る
  cin >> size_ambient_tree;
  rep(i, size_ambient_tree - 1) {
    int a, b;
    cin >> a >> b;
    a--;
    b--; // 入力では1から始まるのでデクリメントする必要がある!
    // edges.push_back(edge{a, b});
    adjs.at(a).push_back(b);
    adjs.at(b).push_back(a);
  }

  // 処理
  // index_adjs.at(initial_root) = -1;
  dfs1(initial_root, -1);
  // dfs2(initial_root, -1);

  // 出力
  rep(i, size_ambient_tree) {
    cout << convert_result(size_ambient_tree, dp2.at(i)) << endl;
  }
}