基本的想法就是对于每个点来一次bfs,求出它所能到达的点中最大的。这样是

对于某个点,如果点能到达它,那么比小的点都不必再搜。

于是可以想到一个优化:反向建边,点能到达点即代表点
到达点

基于上面的想法,只要从大往小搜,已经访问过的点不搜,即可保证答案的合法性。

(这是因为若之前访问过了,那么以前就已经带着比现在更优的信息(即源点编号),现在搜一定不会更优。)

#include<iostream>
#include<cstdio>
#include<queue>
#define maxn 100010
int read();
using namespace std;

queue<int>bfs;
struct Edge{
    int to,nt;
}edge[maxn<<1];int tot,head[maxn];
void add(int a,int b){
    edge[++tot].to=b;
    edge[tot].nt=head[a];
    head[a]=tot;
}
int n,m;
int vis[maxn];
int main(){
    n=read(),m=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read();
        add(y,x);
    }
    for(int k=n;k>=1;k--){//cout<<"i:"<<k<<endl;
    if(!vis[k]){
        while(!bfs.empty())
        bfs.pop();
        bfs.push(k);
        while(!bfs.empty()){
            int no=bfs.front();bfs.pop();

            //cout<<no<<endl;cout<<"To:";
            for(int i=head[no];i;i=edge[i].nt){
                //cout<<edge[i].to<<' ';
                if(edge[i].to<k&&vis[edge[i].to]==0){
                    vis[edge[i].to]=k;
                    bfs.push(edge[i].to);
                }
            }//cout<<endl;
        }
    }
    }
    for(int i=1;i<=n;i++)vis[i]=max(vis[i],i);
    for(int i=1;i<=n;i++)cout<<vis[i]<<' ';
    return 0;
}
int read(){
    int s=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s;
}