#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
int n,m,D[N];
int dfn[N], ori[N], tot,FF[N];
int mn[N], fa[N], d[N];
int in[N];
int F[N][21],idom[N];
vector<int> G[N];//原图
vector<int> P[N];//新图(Topo)
vector<int> T[N];//支配树
vector<int> From[N];//反图
void tarjan(int x,int ff){
dfn[x] = ++tot;
ori[tot] = x;
FF[x] = ff;
if(ff != -1)
P[ff].push_back(x), in[x]++;
for (auto y : G[x])
if(!dfn[y])
tarjan(y,x);
}
void init(){
for(int i = 1;i <= n;i ++)
fa[i] = mn[i] = idom[i] = i;
}
int find(int x){
if(fa[x] == x) return x;
int p = fa[x];
fa[x] = find(fa[x]);
if (dfn[idom[mn[p]]] < dfn[idom[mn[x]]])
mn[x] = mn[p];
return fa[x];
}
void get_idom(){
for(int i = n;i >= 2;i --){
int x = ori[i],mini = n;
if(!x)
continue;
for (auto y : From[x])
{
if(!dfn[y]) continue;
if(dfn[y] < dfn[x]) mini = min(mini,dfn[y]);
else find(y),mini = min(mini,dfn[idom[mn[y]]]);
}
idom[x] = ori[mini], fa[x] = FF[x];
P[idom[x]].push_back(x);
in[x]++;
}
}
int lca(int x,int y){
if(d[x] < d[y]) swap(x,y);
for(int i = 20;i >= 0;i --)
if(d[F[x][i]] >= d[y])
x = F[x][i];
if(x == y) return x;
for(int i = 20;i >= 0;i --)
if(F[x][i] != F[y][i])
x = F[x][i],y = F[y][i];
return F[x][0];
}
void Topo(){
queue<int> q;
for (int i = 1; i <= n;i ++)
if(in[i] == 0) q.push(i);
memset(D, -1, sizeof D);
while (q.size())
{
int u = q.front();
q.pop();
d[u] = d[D[u]] + 1;
F[u][0] = D[u];
for (int i = 1; i <= 20; i++)
F[u][i] = F[F[u][i - 1]][i - 1];
T[D[u]].push_back(u);
for (auto v : P[u])
{
if (D[v] == -1)
D[v] = u;
else
D[v] = lca(u, D[v]);
if (--in[v] == 0)
q.push(v);
}
}
}
int siz[N];
int dfs(int x){
siz[x] = 1;
for(auto y:T[x])
siz[x] += dfs(y);
return siz[x];
}
signed main(){
cin >> n >> m;
for(int i = 1;i <= m;i ++){
int a,b;
cin >> a >> b;
G[a].push_back(b);
From[b].push_back(a);
}
init();
tarjan(1,-1);
get_idom();
Topo();
dfs(1);
for(int i = 1;i <= n;i ++)
cout << siz[i] << ' ';
}
用的是求出半支配点后跑DAG求支配树的写法。