Language: C++

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cassert>
#define FOR(i,a,b) for(int i=(a);i<(b);i++)
#define REP(i,n) FOR(i,0,n)
#define rep(i,n) FOR(i,0,n)
#define DEBUG(x) cout<<#x<<": "<<x<<endl
#define vint vector<int>
#define vdouble vector<double>
#define vstring vector<string>
using namespace std;

#include<map>
#include<set>
#include<queue>

typedef long long ll;
typedef unsigned long long ull;

const int MAX_N = 1000000;
static const int INFTY = (1 << 21);

class Edge{
public:
	int from, to, cost;
	int backedge;
	Edge(int from = 0, int to = 0, int cost = 0) :
		from(from), to(to), cost(cost){
	}

};

class Node{
public:
	vector<Edge> edges;
	int prenum;
	int parent;
	int lowest;
	Node(){
		prenum = -1;
		parent = -1;
	}
};

class Graph{
public:
	vector<Node> nodes;
	int prenum;

	Graph(int n) {
		rep(i, n) { Node n;  nodes.push_back(n); }
		prenum = 0;
	}

	Node operator [] (int i) const{
		return nodes[i];
	}

};

Graph G(0);

void dfs(Edge &e){

	const Node& n = G[e.to];

	if (n.prenum != -1) {
		int parent = G[e.from].parent;
		if (e.to == parent){
			e.backedge = -1; // edge to its parent
		}
		else{
			e.backedge = 1; // backedge
		}
		return;
	}

	// set parameter
	G.prenum++;
	e.backedge = 0; // edge-of-DFStree
	n.prenum = G.prenum;
	n.parent = e.from;
	n.lowest = n.prenum;

	for (Edge &eg : n.edges){
		dfs(eg);
	}

	for (Edge &eg : n.edges){
		// edge-of-DFStree
		if (eg.backedge == 0){
			n.lowest = min(n.lowest, G[eg.to].lowest);
		}
		// backedge
		if (eg.backedge == 1){
			n.lowest = min(n.lowest, G[eg.to].prenum);
		}
	}

}


int main() {

	int V, E;
	cin >> V >> E;

	G = Graph(V);

	rep(i, E){
		int from, to;
		cin >> from >> to;
		G[from].edges.push_back(Edge(from, to, 0));
		G[to].edges.push_back(Edge(to, from, 0));
	}

	Edge e0(-1, 0, 0);
	dfs(e0);

	rep(i, V){
		// printf("%d %d %d %d\n", i, G[i].parent, G[i].prenum, G[i].lowest);
	}

	set<int> ret;

	// root
	rep(i, V){
		if (G[i].parent == -1){
			int count = 0;
			rep(j, V){
				if (G[j].parent == i) count++;
			}
			if (count > 1) ret.insert(i);
		}
	}

	// not root
	rep(i, V){
		if(G[i].parent != -1){
			const Node& n = G[i];
			const Node& p = G[n.parent];
			if (p.prenum <= n.lowest && p.parent != -1){
				ret.insert(n.parent);
			}
		}
	}

	for (int i : ret){
		cout << i << endl;
	}
}

Compiler output:
a.cc: In function 'void dfs(Edge&)':
a.cc:84:18: error: assignment of member 'Node::prenum' in read-only object
   84 |         n.prenum = G.prenum;
      |         ~~~~~~~~~^~~~~~~~~~
a.cc:85:18: error: assignment of member 'Node::parent' in read-only object
   85 |         n.parent = e.from;
      |         ~~~~~~~~~^~~~~~~~
a.cc:86:18: error: assignment of member 'Node::lowest' in read-only object
   86 |         n.lowest = n.prenum;
      |         ~~~~~~~~~^~~~~~~~~~
a.cc:88:27: error: binding reference of type 'Edge&' to 'const Edge' discards qualifiers
   88 |         for (Edge &eg : n.edges){
      |                           ^~~~~
a.cc:92:27: error: binding reference of type 'Edge&' to 'const Edge' discards qualifiers
   92 |         for (Edge &eg : n.edges){
      |                           ^~~~~
a.cc:95:34: error: assignment of member 'Node::lowest' in read-only object
   95 |                         n.lowest = min(n.lowest, G[eg.to].lowest);
      |                         ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
a.cc:99:34: error: assignment of member 'Node::lowest' in read-only object
   99 |                         n.lowest = min(n.lowest, G[eg.to].prenum);
      |                         ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~