set<int> ap;
void findAP(int u, int par, vector<vector<int>> &G, vector<int> &low, vector<int> &time, int timer){
time[u] = low[u] = timer;
// time[u] stores the time at which u was visited, and low[v] stores the minimum time among nodes that
// can be reached from v ( minimum of low[i] of all its adjacent vertex except the parent ). u is an articulation
// point if :
// 1) u is root -> It's children are disconnected ( The dfs calls it has to make to its children > 1 )
// 2) u is not root -> If time[u]<=low[v]
int dfs = 0;
for(auto &v: G[u]){
if(v==par) continue;
if(time[v]==-1){
dfs++;
findBridge(v, u, G, low, time, timer+1, bridges);
if(par!=-1&&time[u]<=low[v])
ap.insert(u);
low[u] = min(low[u], low[v]);
}else{
// Here, in addition to bridge, a node can also try to touch a neighbor which is not
// it's parent.
low[u] = min(low[u], time[v]);
}
}
if(par==-1 && dfs>1) ap.insert(u);
}
vector<vector<int>> AP(int n, vector<vector<int>>& E) {
vector<vector<int>> G(n);
for(auto &e: E){
G[e[0]].push_back(e[1]);
G[e[1]].push_back(e[0]);
}
vector<int> low(n, INT_MAX);
vector<int> time(n, -1);
// Here time also acts as the visited array
for(int u=0;u<n;u++){
if(time[u]==-1)
findAP(u, -1, G, low, time, 0);
}
return ap;
}