/*
author:yjc
time:2014/3/29
title:双连通性算法
双连通:定义DFN(u)为u在搜索树(以下简称为树)中被遍历到的次序号。
定义Low(u)为u或u的子树中能通过非父子边追溯到的最早的节点,即DFN序号最小的节点
Low(u)=Min { DFN(u) DFN(v) (u,v)为后向边(返祖边) 等价于 DFS(v)<DFS(u)且v不为u的父亲节点 Low(v) (u,v)为树枝边(父子边) }
一个顶点u是割点,则满足(1)或(2)
(1)u为树根,且u有多余1个子树
(2)u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得DFS(u)<=Low(v)。
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
const int N=100010;
bool used[N];
int size,id[N];
bool iscut[N];
int dfn[N], low[N], Index, n,m;
struct Pool
{
int pre,k;
}p[100*N];
int v[N],num;
stack<int>sta;
void add(int a,int b)
{
p[++num].pre=v[a];
p[num].k=b;
v[a]=num;
}
void init() {
Index = 0;
memset(v, 0, sizeof (v));
num = 0;
memset(dfn, 0, sizeof (dfn));
memset(iscut, 0, sizeof (iscut));
}
void tarjan(int x) {
dfn[x] = low[x] = ++Index;
for (int tmp = v[x], k; k = p[tmp].k, tmp; tmp = p[tmp].pre)
if (!dfn[k]) {
tarjan(k);
if (dfn[x] <= low[k])
iscut[x] = true;
low[x] = min(low[ x], low[k]);
} else low[x] = min(low[x], dfn[k]);
}
void cutpoint() {
int num = 0;
dfn[1] = Index = 1;
for (int tmp = v[1], k; k = p[tmp].k, tmp; tmp = p[tmp].pre)
if (!dfn[k]) {
num++;
tarjan(k);
}
iscut[1] = num > 1;
}
int main(){
scanf("%d%d",&n,&m);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
// scc();
cutpoint();
for(int i=1;i<=n;i++)
printf("%d ",iscut[i]);
return 0;
}
/*
6 8
1 3
3 5
1 2
3 4
5 6
4 1
2 4
4 6
7 10
1 2
1 3
2 4
2 5
1 4
1 5
3 6
3 7
4 5
6 7
*/
/*
author:yjc
time:2014/3/29
title:强连通性算法
强连通:定义DFN(u)为节点u搜索的次序编号(时间戳),
Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。由定义可以得出,
Low(u)=Min{DFN(u),
Low(v),(u,v)为树枝边,u为v的父节点
DFN(v),(u,v)为指向栈中节点的后向边(非横叉边)}
DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
const int N=100010;
bool used[N];
int size,id[N];
bool iscut[N];
int dfn[N], low[N], Index, n,m;
struct Pool
{
int pre,k;
}p[100*N];
int v[N],num;
stack<int>sta;
void add(int a,int b)
{
p[++num].pre=v[a];
p[num].k=b;
v[a]=num;
}
void init() {
Index = 0;
memset(v, 0, sizeof (v));
num = 0;
memset(dfn, 0, sizeof (dfn));
memset(iscut, 0, sizeof (iscut));
}
//used表示点是否在栈中
//id是点所属SCC的编号
void tarjan(int x)
{
dfn[x]=low[x]=++Index;
sta.push(x);
used[x]=true;
for(int tmp=v[x],k;k=p[tmp].k,tmp;tmp=p[tmp].pre)
if(!dfn[k])
{
tarjan(k);
low[x]=min(low[x],low[k]);
}else if(used[k])low[x]=min(low[x],dfn[k]);
if(dfn[x]==low[x])
{
size++;
int k;
do
{
k=sta.top();
sta.pop();
used[k]=false;
id[k]=size;
}while(k!=x);
}
}
void scc()
{
Index=size=0;
memset(id,0,sizeof(id));
memset(dfn,0,sizeof(dfn));
memset(used,false,sizeof(used));
while(!sta.empty())sta.pop();
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
}
int main(){
scanf("%d%d",&n,&m);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
}
scc();
for(int i=1;i<=n;i++)
printf("%d ",id[i]);
return 0;
}
/*
6 8
1 3
3 5
1 2
3 4
5 6
4 1
2 4
4 6
7 10
1 2
1 3
2 4
2 5
1 4
1 5
3 6
3 7
4 5
6 7
*/