#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
#include<vector>
#include<cstring>
#include<set>
#include <cstdio>
#include<queue>
#include<map>
#define WHITE 0
#define GRAY  1
#define BLACK 2
#define endl '\n'
#define forup(i,a,n) for( int i =a; i < n; i++)
#define llforup(i,a,n) for( ll i =a; i < n; i++)
#define fordown(i,a,n) for( int i =a; i >= n; i--)
#define fornot(i,a,n) for( int i=a;!n;i++)
#define mod  (ll)(1e9+7)
#define ifempty(a)  if(!a)
using namespace std;
typedef long long ll;
inline int readnum()
{
	int p, data = 0; char ch = 0;
	while ((ch != '-') &&( ch < '0' || ch>'9') )//注意运算优先级，逻辑与大于逻辑非
		ch = getchar();//相当于正则表达式的搜索，跳过不需要的字符
	if (ch == '-')
		p = -1, ch = getchar();//不能与if平行放置，不懂为什么，可能是bug
	else p = 1;
	while (ch >= '0' && ch <= '9') data = data * 10 + ch - '0', ch = getchar();//搜索需要的字符
	return data * p;
}
inline int ReadPossitive()
{
	char ch; int data = 0;
	ch = getchar();
	while (ch == ' ' || ch == '\n')ch = getchar();
	while (ch != ' ' && ch != '\n' && ch != EOF)
	{
		data = data * 10 + ch - '0';
		ch = getchar();
	}
	return data;
}
//E-mail 374656045@qq.com
class solve {
private:
	int v, e;
	int* parent, * visorder, * backto;
	bool* vis;
	int cnt;//序号记数
	vector<int>* G;
public://采用邻接表建图
	solve()
	{
		initial();
		CalculateBackto(0, -1);
		FindArticulationPoint();
	}
	void initial()
	{
		v = ReadPossitive(); e = ReadPossitive();
		G = new vector<int>[v];
		parent = new int[v];
		visorder = new int[v]();//初始化少开一个vis数组
		backto = new int[v];
		vis = new bool[v]();
		cnt = 1;
		int u, to;
		forup(i, 0, e)
		{
			u = ReadPossitive(); to = ReadPossitive();
			G[u].push_back(to);
			G[to].push_back(u);//无向图画双边
		}
	}
	void CalculateBackto(int cur,int pre)//采用dfs递归遍历...错！生成dfs树不等于用dfs遍历图，搞清楚概念
	{
		visorder[cur] = backto[cur] = cnt++;//注意根0的visorder是1，这是少开一个vis数组留下来的问题
		vis[cur] = true;
		int next;
		forup(i, 0, G[cur].size())
		{
			next = G[cur][i];
			if (!vis[next])//是父子边
			{
				parent[next] = cur;
				CalculateBackto(next, cur);//找到子树能回溯到的最早结点。即寻找当前通路的替身能到达的最远位置
				if (backto[cur] > backto[next])
					backto[cur] = backto[next];
			}
			else//是回边，next比cur出现得早
			{
				if (next != pre)//一来无环图产生的无意义的“回边”必须略去，二来避免重复计算。1-2-3,1-3;1-3这条回边只在生成1-2-3这棵树中被计算，在root的for循环中不会被重复计算
				{
					if (backto[cur] > visorder[next])//同一个节点的子树中可能有多条回边，所以要比较，更新
						backto[cur] = visorder[next];
				}
			}
		}
	}
	void FindArticulationPoint()
	{
		int rootson = 0; 
		set<int>ap;
		forup(i, 1, v)
			if (parent[i] == 0)
				rootson++;
			else if (visorder[parent[i]] <= backto[i])//两个节点间仅存在父子边，不存在回边
				ap.insert(parent[i]);//是DFS树中的parent

		if (rootson > 1)ap.insert(0);
		for (set<int>::iterator it = ap.begin(); it != ap.end(); it++)
			printf("%d\n", *it);
		//sort(ap,ap+len);//sort中的len是区间长度（元素个数）
		//处理重复元素：要考虑没有元素选入：1 0以及去重等问题，所以还是用set管理比较好
		//int newlen = 1;
		//for(int i=1;i<len;i++)
		//{
		//	if (ap[i] != ap[newlen-1])
		//	{
		//		ap[newlen] = ap[i];//双重指针删除数组元素
		//		newlen++;
		//	}
		//}
		//forup(i, 0, newlen)
		//	printf("%d\n", ap[i]);

	}
};
int main(void)
{
	ios_base::sync_with_stdio(0); cin.tie(0);//慎用，cin和scanf混用会导致非MSC++WA
#ifdef DEBUG
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	solve solution;
#ifdef DEBUG
	fclose(stdin);//关闭文件 
	fclose(stdout);//关闭文件 
#endif
	return 0;

}




Measured memory_bytes: 4732