Problem:
Articulation Points
Find articulation points of a given undirected graph
G(V, E)
.
A vertex in an undirected graph is an articulation point (or cut vertex) iff removing it disconnects the graph.
Input
|V|
|E|
s
0
t
0
s
1
t
1
:
s
|E|-1
t
|E|-1
, where
|V|
is the number of vertices and
|E|
is the number of edges in the graph. The graph vertices are named with the numbers 0, 1,...,
|V|
-1 respectively.
s
i
and
t
i
represent source and target verticess of
i
-th edge (undirected).
Output
A list of
articulation points
of the graph
G
ordered by name.
Constraints
1 ≤
|V|
≤ 100,000
0 ≤
|E|
≤ 100,000
The graph is connected
There are no parallel edges
There are no self-loops
Sample Input 1
4 4
0 1
0 2
1 2
2 3
Sample Output 1
2
Sample Input 2
5 4
0 1
1 2
2 3
3 4
Sample Output 2
1
2
3

Submitted solutions:

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

vector<int> br;
vector<vector<int>> adj;
int Time=0;
vector<int> low, disc;

void dfsBR(int u, int p) {
  low[u] = disc[u] = ++Time; int c=0;
  for (int& v : adj[u]) {
    if (v == p) continue; // we don't want to go back through the same path.
                          // if we go back is because we found another way back
    if (!disc[v]) { // if V has not been discovered before
      dfsBR(v, u);  // recursive DFS call
      if (p!=u && disc[u] <= low[v]) // condition to find a bridge
        br[u]=1;
      low[u] = min(low[u], low[v]); // low[v] might be an ancestor of u
        c++;

    } else // if v was already discovered means that we found an ancestor
      low[u] = min(low[u], disc[v]); // finds the ancestor with the least discovery time
  }
  if(c>1 && p==u) br[u]=1;
}

void BR() {
  br = low = disc = vector<int>(adj.size());
  Time = 0;
  for (int u = 0; u < adj.size(); u++)
    if (!disc[u])
      dfsBR(u, u);
}

int main(){
    int n;
    cin >> n;
    int m;
    cin >> m;
    adj.assign(n+1,{});
    for(int i=0;i<m;i++){
      int x,y;
      cin >> x>> y;
      adj[x].push_back(y);
      adj[y].push_back(x);
    }
    low.assign(n+1,0);
    disc.assign(n+1,0);
    BR();
    for(int i=0;i<n;i++){
        if(br[i]) cout << i << '\n';
    }
}
accuracy: 1
time_ms: 10
memory_kb: 4848
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define MM << ' ' <<
using ll = long long;
using ld = long double;
using pll = pair<ll, ll>;
using vl = vector<ll>;
template <class T> using vec = vector<T>;
template <class T> using vv = vec<vec<T>>;
template <class T> using vvv = vv<vec<T>>;
template <class T> using minpq = priority_queue<T, vector<T>, greater<T>>;
#define rep(i, r) for(ll i = 0; i < (r); i++)
#define reps(i, l, r) for(ll i = (l); i < (r); i++)
#define rrep(i, l, r) for(ll i = (r) - 1; i >= l; i--)
template <class T> void uniq(T &a) { sort(all(a)); erase(unique(all(a)), a.end()); }
#define all(a) (a).begin(), (a).end()
#define sz(a) (ll)(a).size()
const ll INF = numeric_limits<ll>::max() / 4;
const ld inf = numeric_limits<ld>::max() / 2;
const ll mod1 = 1000000007;
const ll mod2 = 998244353;
const ld pi = 3.141592653589793238;
template <class T> void rev(T &a) { reverse(all(a)); }
ll popcnt(ll a) { return __builtin_popcountll(a); }
template<typename T>
bool chmax(T &a, const T& b) { return a < b ? a = b, true : false; }
template<typename T>
bool chmin(T &a, const T& b) { return a > b ? a = b, true : false; }
template <typename T1, typename T2>
ostream& operator<<(ostream& os, const pair<T1, T2>& p) {
	os << p.first << " " << p.second;
	return os;
}
template <typename T1, typename T2>
istream& operator>>(istream& is, pair<T1, T2>& p) {
	is >> p.first >> p.second;
	return is;
}
template <typename T>
ostream& operator<<(ostream& os, const vec<T>& v) {
	rep(i, sz(v)) {
		os << v[i] << " \n"[i + 1 == sz(v)];
	}
	return os;
}
template <typename T>
istream& operator>>(istream& is, vec<T>& v) {
	for (T& in : v) is >> in;
	return is;
}
void yesno(bool t) {
	cout << (t ? "Yes" : "No") << endl;
}
ll modmul(ll a, ll b, ll mod) { return __int128(a) * b % mod; }
ll modpow(ll a, ll b, ll mod) {
  ll ans = 1;
  for (; b; a = modmul(a, a, mod), b /= 2)
    if (b & 1) ans = modmul(ans, a, mod);
  return ans;
}
// a^x == b (mod p), if no such x exists than -1
ll modlog(ll a, ll b, ll p) {
    ll n = (ll)sqrtl(p) + 1, e = 1, f = 1, j = 1;
    unordered_map<ll, ll> A;
    while (j = n && (e = f = e * a % p) != b % p)
        A[e * b % p] = j++;
    if (e == b % p) return j;
    if (gcd(p, e) == gcd(p, b))
        reps(i, 2, n + 2) if (A.count(e = e * f % p)) 
            return n * i - A[e];
    return -1;
}
// x^2 == a (mod p)
ll modsqrt(ll a, ll p) {
    a %= p;
    while (a < 0) a += p;
    if (a == 0) return 0;
    if (modpow(a, (p - 1) / 2, p) != 1) return -1;
    if (p % 4 == 3) return modpow(a, (p + 1) / 4, p);
    ll s = p - 1, n = 2;
    ll r = 0, m;
    while (s % 2 == 0) ++r, s /= 2;
    while (modpow(n, (p - 1) / 2, p) != p - 1) ++n;
    ll x = modpow(a, (s + 1) / 2, p);
    ll b = modpow(a, s, p), g = modpow(n, s, p);
    for (;; r = m) {
        ll t = b;
        for (m = 0; m < r && t != 1; ++m) t = modmul(t, t, p);
        if (m == 0) return x;
        ll gs = modpow(g, 1ll << (r - m - 1), p);
        g = modmul(gs, gs, p);
        x = modmul(x, gs, p);
        b = modmul(b, g, p);
    }
}
ll modinv(ll x, ll mod) { return modpow(x, mod - 2, mod); }
template <typename G>
struct LowLink {
    ll n;
    const G g;
    vl ord, low, arti;
    vec<pll> bridge;

    LowLink(G g) : n(sz(g)), g(g), ord(sz(g), -1), low(sz(g), -1) {
        ll k = 0;
        rep(i, n) {
            if (ord[i] == -1) k = dfs(i, k, -1);
        }
    }

    ll dfs(ll x, ll k, ll p) {
        low[x] = (ord[x] = k++);
        ll cnt = 0;
        bool is_arti = false, second = false;
        for (auto &e : g[x]) {
            if (ord[e] == -1) {
                cnt++;
                k = dfs(e, k, x);
                chmin(low[x], low[e]);
                is_arti |= (p != -1) && (low[e] >= ord[x]);
                if (ord[x] < low[e]) bridge.emplace_back(min(x, e), max(x, e));
            } else if (e != p || second) {
                chmin(low[x], ord[e]);
            } else {
                second = true;
            }
        }
        is_arti |= p == -1 && cnt > 1;
        if (is_arti) arti.emplace_back(x);
        return k;
    }
};
void solve() {
    ll n, m; cin >> n >> m;
	vv<ll> g(n);
	rep(i, m) {
		ll u, v; cin >> u >> v;
		g[u].emplace_back(v);
		g[v].emplace_back(u);
	}
	LowLink<vv<ll>> LL(g);
	auto ans = LL.arti;
	sort(all(ans));
	rep(i, sz(ans)) {
		cout << ans[i] << endl;
	}
}
int main() {
    ll T = 1;
    // cin >> T;
    while (T--) solve();
}
accuracy: 1
time_ms: 10
memory_kb: 8448
final_rank: 15

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Vertex
{
    vector<int> destinations;
    int rank = 0;
    int relativeRank = 0;
    bool isArticlePoint = false;
};

// 子の relativeRank が current のランクより数値が高い場合は関節点として扱う
void searchArticulationPoints(vector<Vertex> &vertexs, int current, int rank)
{
    // current から辿れる頂点を見る
    for (int num : vertexs[current].destinations)
    {
        // 探索した頂点が未探索の場合はその頂点が関節点か調べる
        if (vertexs[num].rank == 0)
        {
            vertexs[num].rank = rank;
            vertexs[num].relativeRank = rank++;
            searchArticulationPoints(vertexs, num, rank);
            vertexs[current].relativeRank = min(vertexs[current].relativeRank, vertexs[num].relativeRank);
            if (vertexs[current].rank <= vertexs[num].relativeRank)
                vertexs[current].isArticlePoint = true;
        }
        else
        {
            // 隣接する点のランクが自分より低い場合はそのランクを relativeRank とする
            vertexs[current].relativeRank = min(vertexs[current].relativeRank, vertexs[num].rank);
        }
    }
}

int main()
{
    int vertexN, edgeN;
    cin >> vertexN >> edgeN;
    vector<Vertex> vertexs(vertexN);

    for (int i = 0; i < edgeN; i++)
    {
        int s, t;
        cin >> s >> t;
        vertexs[s].destinations.push_back(t);
        vertexs[t].destinations.push_back(s);
    }

    // 頂点0を根とする
    int rank = 1;
    vertexs[0].rank = rank;
    vertexs[0].relativeRank = rank++;

    int rootChild = 0; // 根の子の数
    for (int num : vertexs[0].destinations)
    {
        if (vertexs[num].rank == 0)
        {
            vertexs[num].rank = rank;
            vertexs[num].relativeRank = rank++;

            searchArticulationPoints(vertexs, num, rank);
            rootChild++;
        }
    }

    // 根が二つ以上の子を持つ場合は関節点
    if (rootChild > 1)
        vertexs[0].isArticlePoint = true;

    // 関節点を出力
    for (int i = 0; i < vertexN; i++)
        if (vertexs[i].isArticlePoint)
            cout << i << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 4460
final_rank: 2

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Vertex
{
    vector<int> destinations;
    int rank = 0;
    int relativeRank = 0;
    bool isArticlePoint = false;
};

// 子の relativeRank が current のランクより数値が高い場合は関節点として扱う
void searchArticulationPoints(vector<Vertex> &vertexs, int current, int rank)
{
    // current から辿れる頂点を見る
    for (int num : vertexs[current].destinations)
    {
        // 探索した頂点が未探索の場合はその頂点が関節点か調べる
        if (vertexs[num].rank == 0)
        {
            vertexs[num].rank = rank;
            vertexs[num].relativeRank = rank++;
            searchArticulationPoints(vertexs, num, rank);
            vertexs[current].relativeRank = min(vertexs[current].relativeRank, vertexs[num].relativeRank);
            if (vertexs[current].rank <= vertexs[num].relativeRank)
                vertexs[current].isArticlePoint = true;
        }
        else
        {
            // 隣接する点のランクが自分より低い場合はそのランクを relativeRank とする
            vertexs[current].relativeRank = min(vertexs[current].relativeRank, vertexs[num].rank);
        }
    }
}

int main()
{
    int vertexN, edgeN;
    cin >> vertexN >> edgeN;
    vector<Vertex> vertexs(vertexN);

    for (int i = 0; i < edgeN; i++)
    {
        int s, t;
        cin >> s >> t;
        vertexs[s].destinations.push_back(t);
        vertexs[t].destinations.push_back(s);
    }

    // 頂点0を根とする
    int rank = 1;
    vertexs[0].rank = rank;
    vertexs[0].relativeRank = rank++;

    int rootChild = 0; // 根の子の数
    for (int num : vertexs[0].destinations)
    {
        if (vertexs[num].rank == 0)
        {
            vertexs[num].rank = rank;
            vertexs[num].relativeRank = rank++;

            searchArticulationPoints(vertexs, num, rank);
            rootChild++;
        }
        else
            vertexs[0].relativeRank = min(vertexs[0].relativeRank, vertexs[num].rank);
    }

    // 根が二つ以上の子を持つ場合は関節点
    if (rootChild > 1)
        vertexs[0].isArticlePoint = true;

    // 関節点を出力
    for (int i = 0; i < vertexN; i++)
        if (vertexs[i].isArticlePoint)
            cout << i << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 4752
final_rank: 3

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Vertex
{
    vector<int> destinations;
    int rank = 0;
    int relativeRank = 0;
    bool isArticlePoint = false;
};

// current(親) のランクより数値が低いランクを関節点として扱う
void searchArticulationPoints(vector<Vertex> &vertexs, int current, int rank)
{
    // current から辿れる頂点を見つける
    for (int num : vertexs[current].destinations)
    {
        // 探索した頂点が未探索の場合はその頂点が関節点か調べる
        if (vertexs[num].rank == 0)
        {
            vertexs[num].rank = rank;
            vertexs[num].relativeRank = rank++;
            searchArticulationPoints(vertexs, num, rank);
            vertexs[current].relativeRank = min(vertexs[current].relativeRank, vertexs[num].relativeRank);
            if (vertexs[current].rank <= vertexs[num].relativeRank)
                vertexs[current].isArticlePoint = true;
        }
        else
            vertexs[current].relativeRank = min(vertexs[current].relativeRank, vertexs[num].rank);
    }
}

int main()
{
    int vertexN, edgeN;
    cin >> vertexN >> edgeN;
    vector<Vertex> vertexs(vertexN);

    for (int i = 0; i < edgeN; i++)
    {
        int s, t;
        cin >> s >> t;
        vertexs[s].destinations.push_back(t);
        vertexs[t].destinations.push_back(s);
    }

    // 頂点0を根とする
    int rank = 1;
    vertexs[0].rank = rank;
    vertexs[0].relativeRank = rank++;

    int rootChild = 0; // 根の子の数
    for (int num : vertexs[0].destinations)
    {
        if (vertexs[num].rank == 0)
        {
            vertexs[num].rank = rank;
            vertexs[num].relativeRank = rank++;

            searchArticulationPoints(vertexs, num, rank);
            rootChild++;
        }
        else
            vertexs[num].relativeRank = min(vertexs[0].relativeRank, vertexs[num].rank);
    }

    // 根が二つ以上の子を持つ場合は関節点
    if (rootChild > 1)
        vertexs[0].isArticlePoint = true;

    // 関節点を出力
    for (int i = 0; i < vertexN; i++)
        if (vertexs[i].isArticlePoint)
            cout << i << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 4452
final_rank: 1

code_snippet: #include <iostream>
#include <vector>
using namespace std;

struct Vertex
{
    vector<int> destinations;
    int rank = 0;
    bool isArticlePoint = false;
};

// 子から親を経由せずに自分より数値が低いランクを探索する
bool searchUnderRank(const vector<Vertex> &vertexs, vector<bool> &rankSearchedMap, int current, int startRank)
{
    for (int num : vertexs[current].destinations)
    {
        if (!rankSearchedMap[num])
        {
            int numRank = vertexs[num].rank;
            if (numRank != 0 && startRank > numRank)
                return true;
            rankSearchedMap[num] = true;
            if (searchUnderRank(vertexs, rankSearchedMap, num, startRank))
                return true;
        }
    }
    return false;
}

// current(親) のランクより数値が低いランクを関節点として扱う
void searchArticulationPoints(vector<Vertex> &vertexs, int current, int &rank)
{
    // current から辿れる頂点を見つける
    for (int num : vertexs[current].destinations)
    {
        // 探索した頂点が未探索の場合はその頂点が関節点か調べる
        if (vertexs[num].rank == 0)
        {
            vertexs[num].rank = rank++;

            // 親を除いて自身のランクより低いランクが見つからない場合、親は関節点
            if (!vertexs[current].isArticlePoint)
            {
                int n = vertexs.size();
                vector<bool> rankSearchedMap(n);
                rankSearchedMap[current] = true; // 親を探索済みにする
                if (!searchUnderRank(vertexs, rankSearchedMap, num, vertexs[num].rank))
                    vertexs[current].isArticlePoint = true;
            }

            searchArticulationPoints(vertexs, num, rank);
        }
    }
}

int main()
{
    int vertexN, edgeN;
    cin >> vertexN >> edgeN;
    vector<Vertex> vertexs(vertexN);

    for (int i = 0; i < edgeN; i++)
    {
        int s, t;
        cin >> s >> t;
        vertexs[s].destinations.push_back(t);
        vertexs[t].destinations.push_back(s);
    }

    // 頂点0を根とする
    int rank = 1;
    vertexs[0].rank = rank++;

    int rootChild = 0; // 根の子の数
    for (int num : vertexs[0].destinations)
    {
        if (vertexs[num].rank == 0)
        {
            vertexs[num].rank = rank++;

            searchArticulationPoints(vertexs, num, rank);
            rootChild++;
        }
    }

    // 根が二つ以上の子を持つ場合は関節点
    if (rootChild > 1)
        vertexs[0].isArticlePoint = true;

    // 関節点を出力
    for (int i = 0; i < vertexN; i++)
        if (vertexs[i].isArticlePoint)
            cout << i << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 5460
final_rank: 8

code_snippet: #include <iostream>
#include <vector>
using namespace std;

struct Vertex
{
    vector<int> destinations;
    int rank = 0;
    bool isArticlePoint = false;
    bool isSearched = false;
};

// 子から親を経由せずに自分より数値が低いランクを探索する
bool searchUnderRank(const vector<Vertex> &vertexs, vector<bool> &rankSearchedMap, int current, int startRank)
{
    for (int num : vertexs[current].destinations)
    {
        if (!rankSearchedMap[num])
        {
            int numRank = vertexs[num].rank;
            if (numRank != 0 && startRank > numRank)
                return true;
            rankSearchedMap[num] = true;
            if (searchUnderRank(vertexs, rankSearchedMap, num, startRank))
                return true;
        }
    }
    return false;
}

// current(親) のランクより数値が低いランクを関節点として扱う
void searchArticulationPoints(vector<Vertex> &vertexs, int current, int &rank)
{
    // current から辿れる頂点を見つける
    for (int num : vertexs[current].destinations)
    {
        // 探索した頂点が未探索の場合はその頂点が関節点か調べる
        if (!vertexs[num].isSearched)
        {
            vertexs[num].isSearched = true;
            vertexs[num].rank = rank++;

            // 親を除いて自身のランクより低いランクが見つからない場合、親は関節点
            if (!vertexs[current].isArticlePoint)
            {
                int n = vertexs.size();
                vector<bool> rankSearchedMap(n);
                rankSearchedMap[current] = true; // 親を探索済みにする
                if (!searchUnderRank(vertexs, rankSearchedMap, num, vertexs[num].rank))
                    vertexs[current].isArticlePoint = true;
            }

            searchArticulationPoints(vertexs, num, rank);
        }
    }
}

int main()
{
    int vertexN, edgeN;
    cin >> vertexN >> edgeN;
    vector<Vertex> vertexs(vertexN);

    for (int i = 0; i < edgeN; i++)
    {
        int s, t;
        cin >> s >> t;
        vertexs[s].destinations.push_back(t);
        vertexs[t].destinations.push_back(s);
    }

    // 頂点0を根とする
    vertexs[0].isSearched = true;
    int rank = 1;
    vertexs[0].rank = rank++;

    int rootChild = 0; // 根の子の数
    for (int num : vertexs[0].destinations)
    {
        if (!vertexs[num].isSearched)
        {
            vertexs[num].isSearched = true;
            vertexs[num].rank = rank++;

            searchArticulationPoints(vertexs, num, rank);
            rootChild++;
        }
    }

    // 根が二つ以上の子を持つ場合は関節点
    if (rootChild > 1)
        vertexs[0].isArticlePoint = true;

    // 関節点を出力
    for (int i = 0; i < vertexN; i++)
        if (vertexs[i].isArticlePoint)
            cout << i << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 5412
final_rank: 7

code_snippet: #include <bits/stdc++.h>

//#pragma GCC optimize("Ofast")
//#pragma GCC target("avx,avx2,fma")
//#pragma GCC optimization("unroll-loops")

using namespace std;

#define int long long
#define double long double
#define uint unsigned int
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define is2pow(n) (n && (!(n & (n-1))))
#define OPTIMIZE ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);

const int MOD = 1e15 + 7;
const int MAX_N = 2e5;
const int MAX_K = 80;
const long long INF = 1e18;
const double EPS = 1e-6;

int binPow(int base, int pow, int mod) {
    if (pow == 0)
        return 1 % mod;
    int res = 1;
    while (pow != 0) {
        if (pow & 1)
            res = res * base % mod;
        base = base * base % mod;
        pow = pow >> 1;
    }
    return res;
}

struct Edge {
    int from, to, w;

    Edge(int u,int v,int w = 1) {
        from = u, to = v, this->w = w;
    }
};

struct ArticulationPoints {
    // Time: O(n + m), Space: O(n + m)
    int n;
    vector<vector<Edge>> graph;
    vector<int> visited, tup, tin, isArticulationPoint, articulationPoints;

    explicit ArticulationPoints(const vector<vector<Edge>>& g) :
        n(g.size()), graph(g), visited(n, 0), tin(n, 0), tup(n), isArticulationPoint(n, 0) {
    }

    void dfs(int current, int parent = -1, int height = 0) {
        visited[current] = 1, tin[current] = tup[current] = height;
        int cntChildren = 0;
        for (const Edge& edge: graph[current]) {
            if (edge.to == parent)
                continue;
            if (!visited[edge.to]) {
                cntChildren++;
                dfs(edge.to, current, height + 1);
                tup[current] = min(tup[current], tup[edge.to]);
                if ((tup[edge.to] >= tin[current] && parent != -1) || (parent == -1 && cntChildren > 1))
                    isArticulationPoint[current] = 1;
                continue;
            }
            tup[current] = min(tup[current], tin[edge.to]);
        }
    }

    void findPoints() {
        for (int u = 0; u < n; u++)
            if (!visited[u])
                dfs(u);
        for (int i = 0; i < n; i++)
            if (isArticulationPoint[i])
                articulationPoints.push_back(i);
    }
};


void solve() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<vector<Edge>> g(n);
    for (int i = 0, u, v; i < m; i++) {
        cin >> u >> v;
        g[u].emplace_back(u, v);
        g[v].emplace_back(v, u);
    }
    ArticulationPoints finder(g);
    finder.findPoints();
    auto res(finder.articulationPoints);
    sort(begin(res), end(res));
    for (auto u: res) {
        cout << u << endl;
    }
}

#define TES
#define LOCA
#define FIL

int32_t main() {
    OPTIMIZE
#ifdef FILE
    freopen("path_easy.in", "r", stdin); freopen("path_easy.out", "w", stdout);
#endif
#ifdef LOCAL
    freopen("/home/rekname/data/C++ Projects/codedforceProject/input.txt", "r", stdin);
#endif
    int t = 1;
#ifdef TEST
    cin >> t;
#endif
    while (t--) {
        solve();
    }
}
accuracy: 1
time_ms: 10
memory_kb: 10392
final_rank: 16

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Node
{
    Node *parent = nullptr;
    vector<Node *> children;
    int number;
    bool isArticlePoint = false;
};

// 深さ優先探索で木を作る
void makeTree(const vector<vector<int>> &destGraph, vector<bool> &searchedMap, Node *node, int index, vector<int> &rankMap, int &rank)
{
    for (int num : destGraph[index])
    {
        if (!searchedMap[num])
        {
            searchedMap[num] = true;
            Node *newNode = new Node();
            newNode->number = num;
            newNode->parent = node;
            rankMap[num] = rank++;
            node->children.push_back(newNode);
            makeTree(destGraph, searchedMap, newNode, num, rankMap, rank);
        }
    }
}

// 子から親を経由せずに自分より数値が低いランクを探索する
bool search(const vector<vector<int>> &destGraph, vector<bool> &searchedMap, int current, int startNum, const vector<int> &rankMap)
{
    for (int num : destGraph[current])
    {
        if (!searchedMap[num])
        {
            if (rankMap[startNum] > rankMap[num])
                return true;
            searchedMap[num] = true;
            if (search(destGraph, searchedMap, num, startNum, rankMap))
                return true;
        }
    }
    return false;
}

void searchArticulationPoints(Node *node, vector<int> &articulationPoints, const vector<vector<int>> &destGraph, const vector<int> &rankMap)
{
    int n = destGraph.size();
    for (Node *child : node->children)
    {
        searchArticulationPoints(child, articulationPoints, destGraph, rankMap);
        if (node->isArticlePoint)
            continue;
        vector<bool> searchedMap(n);
        searchedMap[node->number] = true;
        if (!search(destGraph, searchedMap, child->number, child->number, rankMap))
        {
            node->isArticlePoint = true;
            articulationPoints.push_back(node->number);
        }
    }
}

int main()
{
    int vertexN, edgeN;
    cin >> vertexN >> edgeN;
    vector<vector<int>> destGraph(vertexN); // 各頂点から伸びる頂点を保存
    for (int i = 0; i < edgeN; i++)
    {
        int s, t;
        cin >> s >> t;
        destGraph[s].push_back(t);
        destGraph[t].push_back(s);
    }

    // 木を作成
    Node *root = new Node();
    vector<bool> searchedMap(vertexN);
    vector<int> rankMap(vertexN);
    searchedMap[0] = true;
    int rank = 1;
    rankMap[0] = rank++;
    root->number = 0;
    makeTree(destGraph, searchedMap, root, 0, rankMap, rank);

    // 関節点を求める
    vector<int> articulationPoints; // 関節点を保存

    if (root->children.size() > 1)
    {
        articulationPoints.push_back(root->number);
        root->isArticlePoint = true;
    }

    for (Node *child : root->children)
        searchArticulationPoints(child, articulationPoints, destGraph, rankMap);

    sort(articulationPoints.begin(), articulationPoints.end());

    for (int p : articulationPoints)
        cout << p << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 6088
final_rank: 10

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Node
{
    Node *parent = nullptr;
    vector<Node *> children;
    int number;
    bool isArticlePoint = false;
};

// 深さ優先探索で木を作る
void makeTree(const vector<vector<int>> &destGraph, vector<bool> &searchedMap, Node *node, int index, vector<int> &rankMap, int &rank)
{
    for (int num : destGraph[index])
    {
        if (!searchedMap[num])
        {
            searchedMap[num] = true;
            Node *newNode = new Node();
            newNode->number = num;
            newNode->parent = node;
            rankMap[num] = rank++;
            node->children.push_back(newNode);
            makeTree(destGraph, searchedMap, newNode, num, rankMap, rank);
        }
    }
}

// 根を探索する
bool search(const vector<vector<int>> &destGraph, vector<bool> &searchedMap, int current, int startNum, const vector<int> &rankMap)
{
    for (int num : destGraph[current])
    {
        if (!searchedMap[num])
        {
            if (rankMap[startNum] > rankMap[num])
                return true;
            searchedMap[num] = true;
            if (search(destGraph, searchedMap, num, startNum, rankMap))
                return true;
        }
    }
    return false;
}

void getArticulationPoints(Node *node, vector<int> &articulationPoints, const vector<vector<int>> &destGraph, const vector<int> &rankMap)
{
    int n = destGraph.size();
    for (Node *child : node->children)
    {
        getArticulationPoints(child, articulationPoints, destGraph, rankMap);
        if (node->isArticlePoint)
            continue;
        vector<bool> searchedMap(n);
        searchedMap[node->number] = true;
        if (!search(destGraph, searchedMap, child->number, child->number, rankMap))
        {
            node->isArticlePoint = true;
            articulationPoints.push_back(node->number);
        }
    }
}

int main()
{
    int vertexN, edgeN;
    cin >> vertexN >> edgeN;
    vector<vector<int>> destGraph(vertexN); // 各頂点から伸びる頂点を保存
    for (int i = 0; i < edgeN; i++)
    {
        int s, t;
        cin >> s >> t;
        destGraph[s].push_back(t);
        destGraph[t].push_back(s);
    }

    // 木を作成
    Node *root = new Node();
    vector<bool> searchedMap(vertexN);
    vector<int> rankMap(vertexN);
    searchedMap[0] = true;
    int rank = 1;
    rankMap[0] = rank++;
    root->number = 0;
    makeTree(destGraph, searchedMap, root, 0, rankMap, rank);

    // 関節点を求める
    vector<int> articulationPoints; // 関節点を保存

    if (root->children.size() > 1)
    {
        articulationPoints.push_back(root->number);
        root->isArticlePoint = true;
    }

    for (Node *child : root->children)
        getArticulationPoints(child, articulationPoints, destGraph, rankMap);

    sort(articulationPoints.begin(), articulationPoints.end());

    for (int p : articulationPoints)
        cout << p << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 6204
final_rank: 11

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Node
{
    Node *parent = nullptr;
    vector<Node *> children;
    int number;
    bool isArticlePoint = false;
};

// 深さ優先探索で木を作る
void makeTree(const vector<vector<int>> &distVec, vector<bool> &map, Node *node, int index)
{
    for (int num : distVec[index])
    {
        if (!map[num])
        {
            map[num] = true;
            Node *newNode = new Node();
            newNode->number = num;
            newNode->parent = node;
            node->children.push_back(newNode);
            makeTree(distVec, map, newNode, num);
        }
    }
}

// 子から親を経由せずに根を探索する
bool search(const vector<vector<int>> &distVec, vector<bool> &map, int current)
{
    for (int num : distVec[current])
    {
        if (num == 0)
            return true;
        if (!map[num])
        {
            map[num] = true;
            if (search(distVec, map, num))
                return true;
        }
    }
    return false;
}

void getArticulationPoints(Node *node, vector<int> &articulationPoints, const vector<vector<int>> &distVec)
{
    int n = distVec.size();
    for (Node *child : node->children)
    {
        getArticulationPoints(child, articulationPoints, distVec);
        if (node->isArticlePoint)
            continue;
        vector<bool> map(n);
        map[node->number] = true;
        if (!search(distVec, map, child->number))
        {
            node->isArticlePoint = true;
            articulationPoints.push_back(node->number);
        }
    }
}

int main()
{
    int vertexN, edgeN;
    cin >> vertexN >> edgeN;
    vector<vector<int>> distVec(vertexN); // 各頂点から伸びる頂点を保存
    for (int i = 0; i < edgeN; i++)
    {
        int s, t;
        cin >> s >> t;
        distVec[s].push_back(t);
        distVec[t].push_back(s);
    }

    // 木を作成
    Node *root = new Node();
    vector<bool> map(vertexN);
    map[0] = true;
    root->number = 0;
    makeTree(distVec, map, root, 0);

    // 関節点を求める
    vector<int> articulationPoints; // 関節点を保存

    if (root->children.size() > 1)
    {
        articulationPoints.push_back(root->number);
        root->isArticlePoint = true;
    }
    for (Node *child : root->children)
        getArticulationPoints(child, articulationPoints, distVec);

    sort(articulationPoints.begin(), articulationPoints.end());

    for (int p : articulationPoints)
        cout << p << endl;
}
accuracy: 1
time_ms: 310
memory_kb: 6364
final_rank: 17

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

#define int long long
#define rep(i,n) for(int i=0;i<(n);i++)
#define pii pair<int,int>
#define pipii pair<int,pii>
template<typename T> bool chmax(T& a, const T& b) { if (a < b) { a = b; return 1; } return 0; }
template<typename T> bool chmin(T& a, const T& b) { if (a > b) { a = b; return 1; } return 0; }
#define all(v) (v).begin(),(v).end()
#define bit(i) (1LL<<(i))
#define test(b, i) (((b) >> i) & 1)

struct LowLink{
private:
    vector<vector<int>> G;
    int cnt = 0;
    vector<bool> _is_art;

    void DFS(int v, int p=-1){
        low[v] = ord[v] = ++cnt;
        int n_ch = 0;
        for(int u : G[v])if(u != p){
            if(ord[u] == 0){
                DFS(u, v);
                low[v] = min(low[v], low[u]);
                n_ch++;
                if(p != -1 && ord[v] <= low[u])_is_art[v] = true;
            }
            else low[v] = min(low[v], ord[u]);
        }

        if(p == -1 && n_ch >= 2)_is_art[v] = true;

        return;
    }

public:
    vector<int> ord, low;

    LowLink(int N){
        G.resize(N + 1);
        _is_art.resize(N + 1);
        ord.resize(N + 1);
        low.resize(N + 1);
        return;
    }

    void add_edge(int a, int b){
        if(G.size() < max(a, b) + 1){
            G.resize(max(a, b) + 1);
            _is_art.resize(max(a, b) + 1);
            ord.resize(max(a, b) + 1);
            low.resize(max(a, b) + 1);
        }

        G[a].push_back(b);
        G[b].push_back(a);

        return;
    }

    void build(){
        for(int v = 0; v < G.size(); v++)if(ord[v] == 0){
            DFS(v);
        }

        return;
    }

    bool is_art(int n){ return _is_art[n]; }

    bool is_edge(int a, int b){
        if(ord[a] > ord[b])swap(a, b);
        return ord[a] < low[b];
    }

    bool is_edge(pair<int,int> e){
        return is_edge(e.first, e.second);
    }
};

const int MAXN = 1e5 + 10;
const int MAXM = 1e5 + 10;

int N, M;

signed main(){
    cin >> N >> M;
    LowLink ll(N);
    rep(i, M){
        int a, b;
        cin >> a >> b;
        ll.add_edge(a, b);
    }

    ll.build();

    rep(i, N)if(ll.is_art(i))cout << i << endl;

    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 5672
final_rank: 9

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Node
{
    Node *parent = nullptr;
    vector<Node *> children;
    int number;
    bool isBackedge = false;
    bool isArticlePoint = false;
};

// 深さ優先探索で木を作る
void makeTree(const vector<vector<int>> &distVec, vector<bool> &map, Node *node, int index)
{
    for (int num : distVec[index])
    {
        if (!map[num])
        {
            map[num] = true;
            Node *newNode = new Node();
            newNode->number = num;
            newNode->parent = node;
            node->children.push_back(newNode);
            makeTree(distVec, map, newNode, num);
        }
    }
}

// 子から親を経由せずに根を探索する
bool search(const vector<vector<int>> &distVec, vector<bool> &map, int current)
{
    for (int num : distVec[current])
    {
        if (num == 0)
            return true;
        if (!map[num])
        {
            map[num] = true;
            if (search(distVec, map, num))
                return true;
        }
    }
    return false;
}

void getArticulationPoints(Node *node, vector<int> &articulationPoints, const vector<vector<int>> &distVec)
{
    int n = distVec.size();
    for (Node *child : node->children)
    {
        getArticulationPoints(child, articulationPoints, distVec);
        if (node->isArticlePoint)
            continue;
        vector<bool> map(n);
        map[node->number] = true;
        // if (distVec[node->number].size() == 1 && child->isBackedge)
        //     node->isBackedge = true;
        if (!search(distVec, map, child->number))
        {
            node->isArticlePoint = true;
            articulationPoints.push_back(node->number);
        }
    }
    if (node->children.size() == 0)
    {
        vector<bool> map(n);
        map[node->parent->number] = true;
        node->isBackedge = search(distVec, map, node->number);
    }
}

int main()
{
    int vertexN, edgeN;
    cin >> vertexN >> edgeN;
    vector<vector<int>> distVec(vertexN); // 各頂点から伸びる頂点を保存
    for (int i = 0; i < edgeN; i++)
    {
        int s, t;
        cin >> s >> t;
        distVec[s].push_back(t);
        distVec[t].push_back(s);
    }

    // 木を作成
    Node *root = new Node();
    vector<bool> map(vertexN);
    map[0] = true;
    root->number = 0;
    makeTree(distVec, map, root, 0);

    // 関節点を求める
    vector<int> articulationPoints; // 関節点を保存

    if (root->children.size() > 1)
    {
        articulationPoints.push_back(root->number);
        root->isArticlePoint = true;
    }
    for (Node *child : root->children)
        getArticulationPoints(child, articulationPoints, distVec);

    sort(articulationPoints.begin(), articulationPoints.end());

    for (int p : articulationPoints)
        cout << p << endl;
}
accuracy: 1
time_ms: 330
memory_kb: 6048
final_rank: 18

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Node
{
    Node *parent = nullptr;
    vector<Node *> children;
    int number;
    bool isBackedge = false;
    bool isArticlePoint = false;
};

// 深さ優先探索で木を作る
void makeTree(const vector<vector<int>> &distVec, vector<bool> &map, Node *node, int index)
{
    for (int num : distVec[index])
    {
        if (!map[num])
        {
            map[num] = true;
            Node *newNode = new Node();
            newNode->number = num;
            newNode->parent = node;
            node->children.push_back(newNode);
            makeTree(distVec, map, newNode, num);
        }
    }
}

// 葉から親を経由せずに根を探索する
bool search(const vector<vector<int>> &distVec, vector<bool> &map, int current)
{
    for (int num : distVec[current])
    {
        if (num == 0)
            return true;
        if (!map[num])
        {
            map[num] = true;
            if (search(distVec, map, num))
                return true;
        }
    }
    return false;
}

void getArticulationPoints(Node *node, vector<int> &articulationPoints, const vector<vector<int>> &distVec)
{
    int n = distVec.size();
    for (Node *child : node->children)
    {
        getArticulationPoints(child, articulationPoints, distVec);
        if (node->isArticlePoint)
            continue;
        vector<bool> map(n);
        map[node->number] = true;
        if (distVec[node->number].size() == 1 && child->isBackedge)
            node->isBackedge = true;
        else if (!search(distVec, map, child->number))
        {
            node->isArticlePoint = true;
            articulationPoints.push_back(node->number);
        }
    }
    if (node->children.size() == 0)
    {
        vector<bool> map(n);
        map[node->parent->number] = true;
        if (search(distVec, map, node->number))
            node->isBackedge = true;
    }
}

int main()
{
    int vertexN, edgeN;
    cin >> vertexN >> edgeN;
    vector<vector<int>> distVec(vertexN); // 各頂点から伸びる頂点を保存
    for (int i = 0; i < edgeN; i++)
    {
        int s, t;
        cin >> s >> t;
        distVec[s].push_back(t);
        distVec[t].push_back(s);
    }

    // 木を作成
    Node *root = new Node();
    vector<bool> map(vertexN);
    map[0] = true;
    root->number = 0;
    makeTree(distVec, map, root, 0);

    // 関節点を求める
    vector<int> articulationPoints; // 関節点を保存

    if (root->children.size() > 1)
    {
        articulationPoints.push_back(root->number);
        root->isArticlePoint = true;
    }
    for (Node *child : root->children)
        getArticulationPoints(child, articulationPoints, distVec);

    sort(articulationPoints.begin(), articulationPoints.end());

    for (int p : articulationPoints)
        cout << p << endl;
}
accuracy: 1
time_ms: 330
memory_kb: 6276
final_rank: 19

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Node
{
    Node *parent = nullptr;
    vector<Node *> children;
    int number;
    bool isBackedge = false;
};

// 深さ優先探索で木を作る
void makeTree(const vector<vector<int>> &distVec, vector<bool> &map, Node *node, int index)
{
    for (int num : distVec[index])
    {
        if (!map[num])
        {
            map[num] = true;
            Node *newNode = new Node();
            newNode->number = num;
            newNode->parent = node;
            node->children.push_back(newNode);
            makeTree(distVec, map, newNode, num);
        }
    }
}

// 葉から親を経由せずに根を探索する
bool search(const vector<vector<int>> &distVec, vector<bool> &map, int current)
{
    for (int num : distVec[current])
    {
        if (num == 0)
            return true;
        if (!map[num])
        {
            map[num] = true;
            if (search(distVec, map, num))
                return true;
        }
    }
    return false;
}

void getArticulationPoints(Node *node, vector<int> &articulationPoints, const vector<vector<int>> &distVec)
{
    int n = distVec.size();
    for (Node *child : node->children)
    {
        getArticulationPoints(child, articulationPoints, distVec);
        vector<bool> map(n);
        map[node->number] = true;
        if (distVec[node->number].size() == 1 && child->isBackedge)
            node->isBackedge = true;
        else if (!search(distVec, map, child->number))
            articulationPoints.push_back(node->number);
    }
    if (node->children.size() == 0)
    {
        vector<bool> map(n);
        map[node->parent->number] = true;
        if (search(distVec, map, node->number))
            node->isBackedge = true;
    }
}

int main()
{
    int vertexN, edgeN;
    cin >> vertexN >> edgeN;
    vector<vector<int>> distVec(vertexN); // 各頂点から伸びる頂点を保存
    for (int i = 0; i < edgeN; i++)
    {
        int s, t;
        cin >> s >> t;
        distVec[s].push_back(t);
        distVec[t].push_back(s);
    }

    // 木を作成
    Node *root = new Node();
    vector<bool> map(vertexN);
    map[0] = true;
    root->number = 0;
    makeTree(distVec, map, root, 0);

    // 関節点を求める
    vector<int> articulationPoints; // 関節点を保存

    if (root->children.size() > 1)
        articulationPoints.push_back(root->number);
    for (Node *child : root->children)
        getArticulationPoints(child, articulationPoints, distVec);

    sort(articulationPoints.begin(), articulationPoints.end());
    articulationPoints.erase(unique(articulationPoints.begin(), articulationPoints.end()), articulationPoints.end());

    for (int p : articulationPoints)
        cout << p << endl;
}
accuracy: 1
time_ms: 340
memory_kb: 6360
final_rank: 20

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 再帰DFS（Low-Link計算）
void dfs(int u, const vector<vector<int>> &g,
         vector<int> &ord, vector<int> &low, vector<int> &parent,
         int &timer)
{
    ord[u] = low[u] = timer++; // はじめは ord も low も同じ値

    for (int v : g[u])
    {
        if (ord[v] == -1) // v が未訪問のとき
        {
            parent[v] = u;
            dfs(v, g, ord, low, parent, timer);

            low[u] = min(low[u], low[v]); // 子から戻ってきたときに low を更新
        }
        else // v がすでに訪問済み
        {
            low[u] = min(low[u], ord[v]); // 後退辺による更新
        }
    }
}

int main()
{
    int n, m;
    cin >> n >> m;

    // グラフ
    vector<vector<int>> g(n);
    for (int i = 0; i < m; ++i)
    {
        int s, t;
        cin >> s >> t;
        g[s].push_back(t);
        g[t].push_back(s);
    }

    // DFS 用の配列
    vector<int> ord(n, -1), low(n, -1), parent(n, -1);
    int timer = 0;

    // DFS 実行
    dfs(0, g, ord, low, parent, timer);

    // DFS 終了後に関節点を判定
    vector<int> is_arti(n, false);

    // 根に対する判定
    int child_cnt = 0;
    for (int v : g[0])
    {
        if (parent[v] == 0)
            child_cnt++;
    }
    if (child_cnt >= 2) // 子が2つ以上なら関節点
        is_arti[0] = true;

    // 根以外に対する判定
    for (int u = 1; u < n; ++u)
    {
        // 子 v に対して ord[u] <= low[v] を満たすものがあれば関節点
        for (int v : g[u])
        {
            if (parent[v] == u && ord[u] <= low[v])
            {
                is_arti[u] = true;
            }
        }
    }

    // 関節点を昇順に出力
    for (int i = 0; i < n; ++i)
    {
        if (is_arti[i])
            cout << i << '\n';
    }

    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 4780
final_rank: 4

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 再帰DFS（Low-Link計算）
void dfs(int u, const vector<vector<int>> &g,
         vector<int> &ord, vector<int> &low, vector<int> &parent,
         int &timer)
{
    ord[u] = low[u] = timer++; // はじめは ord も low も同じ値

    for (int v : g[u])
    {
        if (ord[v] == -1) // v が未訪問のとき
        {
            parent[v] = u;
            dfs(v, g, ord, low, parent, timer);

            low[u] = min(low[u], low[v]); // 子から戻ってきたときに low を更新
        }
        else // v がすでに訪問済み
        {
            low[u] = min(low[u], ord[v]); // 後退辺による更新
        }
    }
}

int main()
{
    int n, m;
    cin >> n >> m;

    // グラフ
    vector<vector<int>> g(n);
    for (int i = 0; i < m; ++i)
    {
        int s, t;
        cin >> s >> t;
        g[s].push_back(t);
        g[t].push_back(s);
    }

    // DFS 用の配列
    vector<int> ord(n, -1), low(n, -1), parent(n, -1);
    int timer = 0;

    // DFS 実行
    dfs(0, g, ord, low, parent, timer);

    // DFS 終了後に関節点を判定
    vector<int> is_arti(n, false);

    for (int u = 0; u < n; ++u)
    {
        // 根のときの特別判定：子が2つ以上なら関節点
        if (parent[u] == -1)
        {
            int child_cnt = 0;
            for (int v : g[u])
            {
                if (parent[v] == u)
                    child_cnt++;
            }
            if (child_cnt >= 2)
                is_arti[u] = true;
        }
        else
        {
            // 非根のとき：子 v に対して ord[u] <= low[v] を満たすものがあれば関節点
            for (int v : g[u])
            {
                if (parent[v] == u && ord[u] <= low[v])
                {
                    is_arti[u] = true;
                }
            }
        }
    }

    // 関節点を昇順に出力
    for (int i = 0; i < n; ++i)
    {
        if (is_arti[i])
            cout << i << '\n';
    }

    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 5048
final_rank: 6

code_snippet: #include <iostream>
#include <vector>
#include <list>
#include <algorithm>

using Graph = std::vector<std::list<int>>;  // 隣接リスト
using Table = std::vector<int>;             // 頂点番号をキーにした表

// 深さ優先探索して、子を返す
void dfs(const Graph& graph, int u, Table& prenum, Table& parent, Table& lowest, std::vector<bool>& result, int order) {
    lowest[u] = prenum[u] = ++order;    // 見つけた順に番号を割り振る
    int children = 0;
    for (int c : graph[u]) {
        // 訪問済みならBackedge
        if (prenum[c]) {
            lowest[u] = std::min(lowest[u], prenum[c]); // Backedgeの内小さい値を採用
            continue;   // これ以上深く探さない
        }
        // 未訪問なら子
        ++children;
        parent[c] = u; // 親を対応付ける
        dfs(graph, c, prenum, parent, lowest, result, order);
        lowest[u] = std::min(lowest[u], lowest[c]); // 子の方が小さければ採用
        result[u] = result[u] || (prenum[u] <= lowest[c]); // 子の方が大きいなら間接点
    }
    // ルートが2つ以上の子を持つなら、関節点
    result[u] = parent[u] == -1 ? children >= 2 : result[u];
}
std::vector<bool> getArticulationTable(const Graph& graph) {
    // 深さ優先で訪問順序と親番号を記録する
    Table prenum(graph.size(), 0);  // 未訪問は0
    Table parent(graph.size(), -1); // ルートの親は -1 
    Table lowest(graph.size());
    std::vector<bool> result(graph.size(), false);
    dfs(graph, 0, prenum, parent, lowest, result, 0);
    return result;
}

int main() {
    int V, E;
    std::cin >> V >> E;

    Graph graph(V);
    for (int i = 0; i < E; ++i) {
        int s, t;
        std::cin >> s >> t;
        graph[s].push_back(t);
        graph[t].push_back(s);
    }

    auto result = getArticulationTable(graph);

    for (int i = 0; i < V; ++i) {
        if (result[i]) {
            std::cout << i << "\n";
        }
    }
}
accuracy: 1
time_ms: 10
memory_kb: 7808
final_rank: 13

code_snippet: #include <iostream>
#include <vector>
#include <list>
#include <algorithm>

using Graph = std::vector<std::list<int>>;  // 隣接リスト
using Table = std::vector<int>;             // 頂点番号をキーにした表

// 深さ優先探索して、子を返す
void dfs(const Graph& graph, int u, Table& prenum, Table& parent, Table& lowest, std::vector<bool>& result, int order) {
    prenum[u] = ++order;    // 見つけた順に番号を割り振る
    lowest[u] = prenum[u];
    int children = 0;
    for (int c : graph[u]) {
        // 訪問済みならBackedge
        if (prenum[c]) {
            lowest[u] = std::min(lowest[u], prenum[c]); // Backedgeの内小さい値を採用
            continue;   // これ以上深く探さない
        }
        // 未訪問なら子
        ++children;
        parent[c] = u; // 親を対応付ける
        dfs(graph, c, prenum, parent, lowest, result, order);
        lowest[u] = std::min(lowest[u], lowest[c]); // 子の方が小さければ採用
        result[u] = result[u] || (prenum[u] <= lowest[c]); // 子の方が大きいなら間接点
    }
    // ルートが2つ以上の子を持つなら、関節点
    result[u] = parent[u] == -1 ? children >= 2 : result[u];
}
std::vector<bool> getArticulationTable(const Graph& graph) {
    // 深さ優先で訪問順序と親番号を記録する
    Table prenum(graph.size(), 0);  // 未訪問は0
    Table parent(graph.size(), -1); // ルートの親は -1 
    Table lowest(graph.size());
    std::vector<bool> result(graph.size(), false);
    dfs(graph, 0, prenum, parent, lowest, result, 0);
    return result;
}

int main() {
    int V, E;
    std::cin >> V >> E;

    Graph graph(V);
    for (int i = 0; i < E; ++i) {
        int s, t;
        std::cin >> s >> t;
        graph[s].push_back(t);
        graph[t].push_back(s);
    }

    auto result = getArticulationTable(graph);

    for (int i = 0; i < V; ++i) {
        if (result[i]) {
            std::cout << i << "\n";
        }
    }
}
accuracy: 1
time_ms: 10
memory_kb: 7604
final_rank: 12

code_snippet: #include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <ranges>

using Graph = std::vector<std::list<int>>;  // 隣接リスト
using Table = std::vector<int>;             // 頂点番号をキーにした表

// 深さ優先探索して、子を返す
void dfs(const Graph& graph, int s, Table& prenum, Table& parent, Table& lowest, std::vector<bool>& result, int order) {
    lowest[s] = prenum[s] = ++order;    // 見つけた順に番号を割り振る
    int children = 0;
    for (int t : graph[s]) {
        // 訪問済みならBackedge
        if (prenum[t]) {
            lowest[s] = std::min(lowest[s], prenum[t]);
            continue;   // それ以上探さない
        }
        ++children;
        parent[t] = s;              // 自身と親を対応付ける
        dfs(graph, t, prenum, parent, lowest, result, order);
        lowest[s] = std::min(lowest[s], lowest[t]); // 子の方が小さければ採用
    }
    // root は子の数で判定する
    if (parent[s] == -1) {
        // ルートが2つ以上の子を持つなら、関節点
        result[s] = children >= 2; 
        return;
    }
    int p = parent[s];
    // 親がlowestより小さいならbackedgeがないので、親は関節点
    if (prenum[p] <= lowest[s])
        result[p] = true;
}
std::vector<bool> getArticulationTable(const Graph& graph) {
    // 深さ優先で訪問順序と親番号を記録する
    Table prenum(graph.size(), 0);
    Table parent(graph.size(), -1);
    Table lowest(graph.size(), 0);
    std::vector<bool> result(graph.size(), false);
    int order = 0;
    dfs(graph, 0, prenum, parent, lowest, result, order);
    return result;
}

int main() {
    int V, E;
    std::cin >> V >> E;

    Graph graph(V);
    for (int i = 0; i < E; ++i) {
        int s, t;
        std::cin >> s >> t;
        graph[s].push_back(t);
        graph[t].push_back(s);
    }

    auto result = getArticulationTable(graph);

    for (int i = 0; i < V; ++i) {
        if (result[i]) {
            std::cout << i << "\n";
        }
    }
}
accuracy: 1
time_ms: 10
memory_kb: 8104
final_rank: 14