Problem p02902 (C++)

Buggy submission (status: Runtime Error):
/**
 * 内部により小さなループを持たない、ループを1個探せばよい
 */
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

template <class T> bool contains(unordered_set<T> s, const T &t) {
  const auto &ite = s.find(t);
  return ite != s.end();
}

// 深さ優先探索で探索済み
unordered_set<int> not_check;

// 探したループをリストに保存
vector<int> loops;
void make_loops(stack<int> &ancestors, int current) {
  loops.emplace_back(current);
  while (ancestors.top() != current) {
    loops.emplace_back(ancestors.top());
    ancestors.pop();
  }
  reverse(loops.begin(), loops.end());
}

// ループを探す
bool check_loop(const vector<vector<int>> &edges, int current,
                stack<int> &ancestors, unordered_set<int> &ancestors_for_find) {
  // cout << "current:" << current << endl;

  if (contains(ancestors_for_find, current)) {
    make_loops(ancestors, current);
    return true; // ループ発見
  }

  if (!contains(not_check, current)) {
    return false;
  }
  not_check.erase(current);

  // 到達を記録
  ancestors_for_find.emplace(current);
  ancestors.emplace(current);

  for (const auto &next : edges[current]) {
    // cout << "add next: " << next << endl;

    const auto &child_result =
        check_loop(edges, next, ancestors, ancestors_for_find);
    if (child_result) {
      return child_result;
    }
  }

  // 戻り際に到達記録を除去
  ancestors_for_find.erase(current);
  ancestors.pop();
  return false;
}

// ループを縮める
vector<int> shrink_loop(const vector<vector<int>> &edges) {
  vector<int> result;

  unordered_map<int, int> loop_indexes;
  for (int l = 0; l < loops.size(); l++) {
    loop_indexes.emplace(loops[l], l);
  }

  int i = 0;
  while (i < loops.size()) {
    int current = loops[i];
    result.emplace_back(current);
    // cout << "i:" << i << endl;

    // 点集合の中で、最後の辺を結果に追加する
    size_t max_next = 0;
    for (const auto &next : edges[current]) {
      if (loop_indexes.find(next) != loop_indexes.end()) {
        // cout << "next:" << next << endl;
        // cout << "loop_indexes[next]:" << loop_indexes[next] << endl;
        max_next = max(max_next,
                       (loop_indexes[next] - i + loops.size()) % loops.size());
        // cout << "(loop_indexes[next] - i + loops.size()):" <<
        // (loop_indexes[next] - i + loops.size()) << endl; cout << "max_next:"
        // << max_next << endl;
      }
    }

    max_next = (max_next + i) % loops.size();
    // cout << "max_next:" << max_next << endl;

    if (max_next < i) {
      // ループした場合、スキップした部分を結果から削除しておしまい
      result.erase(result.begin(), result.begin() + max_next);
      break;
    }

    for (int j = i + 1; j < max_next; j++) {
      // スキップした部分を候補から外しておく
      loop_indexes.erase(loops[j]);
    }

    i = max_next;
  }

  return result;
}

int main() {
  int n, m, a, b;
  cin >> n >> m;
  vector<vector<int>> edges(n + 1);

  for (int i = 0; i < m; i++) {
    cin >> a >> b;
    edges[a].emplace_back(b);
  }

  for (int i = 1; i <= n; i++) {
    not_check.emplace(i);
  }

  bool loop_found = false;

  while (
      !not_check
           .empty()) { // 一度の探索で終わらない場合、別の箇所から探索しなおし
    const auto &root = not_check.begin();

    stack<int> s;
    unordered_set<int> us;

    loop_found = check_loop(edges, *root, s, us);
    if (loop_found) {
      break;
    }
  }

  if (!loop_found) {
    cout << -1 << endl;
    return 0;
  }

  /*
  cout << "loop:" << endl;
  for (const auto& l : loops) {
    cout << l << endl;
  }
  */

  const auto &shrinked_loops = shrink_loop(edges);

  cout << shrinked_loops.size() << endl;
  for (const auto &l : shrinked_loops) {
    cout << l << endl;
  }
}


Fixed submission:
/**
 * 内部により小さなループを持たない、ループを1個探せばよい
 */
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

template <class T> bool contains(unordered_set<T> s, const T &t) {
  const auto &ite = s.find(t);
  return ite != s.end();
}

// 深さ優先探索で探索済み
unordered_set<int> not_check;

// 探したループをリストに保存
vector<int> loops;
void make_loops(stack<int> &ancestors, int current) {
  loops.emplace_back(current);
  while (ancestors.top() != current) {
    loops.emplace_back(ancestors.top());
    ancestors.pop();
  }
  reverse(loops.begin(), loops.end());
}

// ループを探す
bool check_loop(const vector<vector<int>> &edges, int current,
                stack<int> &ancestors, unordered_set<int> &ancestors_for_find) {
  // cout << "current:" << current << endl;

  if (contains(ancestors_for_find, current)) {
    make_loops(ancestors, current);
    return true; // ループ発見
  }

  if (!contains(not_check, current)) {
    return false;
  }
  not_check.erase(current);

  // 到達を記録
  ancestors_for_find.emplace(current);
  ancestors.emplace(current);

  for (const auto &next : edges[current]) {
    // cout << "add next: " << next << endl;

    const auto &child_result =
        check_loop(edges, next, ancestors, ancestors_for_find);
    if (child_result) {
      return child_result;
    }
  }

  // 戻り際に到達記録を除去
  ancestors_for_find.erase(current);
  ancestors.pop();
  return false;
}

// ループを縮める
vector<int> shrink_loop(const vector<vector<int>> &edges) {
  vector<int> result;

  unordered_map<int, int> loop_indexes;
  for (int l = 0; l < loops.size(); l++) {
    loop_indexes.emplace(loops[l], l);
  }

  int i = 0;
  while (i < loops.size()) {
    int current = loops[i];
    result.emplace_back(current);
    // cout << "i:" << i << endl;

    // 点集合の中で、最後の辺を結果に追加する
    size_t max_next = 0;
    for (const auto &next : edges[current]) {
      if (loop_indexes.find(next) != loop_indexes.end()) {
        // cout << "next:" << next << endl;
        // cout << "loop_indexes[next]:" << loop_indexes[next] << endl;
        max_next = max(max_next,
                       (loop_indexes[next] - i + loops.size()) % loops.size());
        // cout << "(loop_indexes[next] - i + loops.size()):" <<
        // (loop_indexes[next] - i + loops.size()) << endl; cout << "max_next:"
        // << max_next << endl;
      }
    }

    max_next = (max_next + i) % loops.size();
    // cout << "max_next:" << max_next << endl;

    if (max_next < i) {
      // ループした場合、スキップした部分を結果から削除しておしまい
      result.erase(result.begin(),
                   find(result.begin(), result.end(), loops[max_next]));
      break;
    }

    for (int j = i + 1; j < max_next; j++) {
      // スキップした部分を候補から外しておく
      loop_indexes.erase(loops[j]);
    }

    i = max_next;
  }

  return result;
}

int main() {
  int n, m, a, b;
  cin >> n >> m;
  vector<vector<int>> edges(n + 1);

  for (int i = 0; i < m; i++) {
    cin >> a >> b;
    edges[a].emplace_back(b);
  }

  for (int i = 1; i <= n; i++) {
    not_check.emplace(i);
  }

  bool loop_found = false;

  while (
      !not_check
           .empty()) { // 一度の探索で終わらない場合、別の箇所から探索しなおし
    const auto &root = not_check.begin();

    stack<int> s;
    unordered_set<int> us;

    loop_found = check_loop(edges, *root, s, us);
    if (loop_found) {
      break;
    }
  }

  if (!loop_found) {
    cout << -1 << endl;
    return 0;
  }

  /*
  cout << "loop:" << endl;
  for (const auto& l : loops) {
    cout << l << endl;
  }
  */

  const auto &shrinked_loops = shrink_loop(edges);

  cout << shrinked_loops.size() << endl;
  for (const auto &l : shrinked_loops) {
    cout << l << endl;
  }
}