#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define int ll

#define REP(i,n) for(ll i=0;i<n;++i)
#define SORT(name) sort(name.begin(), name.end())
#define ZERO(p) memset(p, 0, sizeof(p))
#define MINUS(p) memset(p, -1, sizeof(p))
#if 1
#  define DBG(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#  define DBG(fmt, ...)
#endif

const ll LLINF = (1LL<<60);
const int INF = (1LL<<30);
const double DINF = std::numeric_limits<double>::infinity();
const int MOD = 1000000007;
#define MAX_N 100010

#define USE_TARJAN

// Kahn のアルゴリズム O(E + V)
// 頂点 id の基数は 0 とする
class Kahn {
    public:
        // n : 頂点数
        Kahn(int n) { Init(n); }
        Kahn() {}
        void Init(int n) {
            m_N = n;
            m_G.assign(m_N, vector<int>());
            m_G2.assign(m_N, vector<int>());
            m_Indeg.assign(m_N, 0);
            m_Zeros = priority_queue< pair<int, int> >();
            m_Ans.clear();
        }

        // 頂点 from から頂点 to へ行く辺を追加
        void AddEdge(int from, int to) {
            m_G[from].push_back(to);
            m_G2[to].push_back(from);
        }

        // O(E + V)
        // トポロジカルソートに成功したら true
        // グラフが DAG でない場合は false
        //
        // 頂点に優先度をつけたトポロジカルソートも可能。
        // m_Zeros の first に入れる優先度を設定すると良い
        bool TopSort() {
            // 各頂点の入次数を計算 O(E)
            for(int i = 0; i < m_N; ++i) {
                m_Indeg[i] = static_cast<int>(m_G2[i].size());
            }
            // 入次数 0 の頂点を stack に入れる O(V)
            for(int i = 0; i < m_N; ++i) {
                // !!! 入れ方に優先度を持たせたい場合はここを工夫する !!!
                if(m_Indeg[i] == 0) { m_Zeros.push(make_pair(777, i)); }
            }
            while(m_Zeros.size() > 0) {
                // 入次数が 0 の頂点をとってくる
                pair<int, int> u = m_Zeros.top(); m_Zeros.pop();
                m_Ans.push_back(u.second);      // 入次数 0 の頂点を結果列に挿入
                // この頂点から伸びている辺を削除。新たに入次数 0 になるものがあるなら追加
                for(auto& v : m_G[u.second]) {
                    m_Indeg[v]--;
                    // !!! 入れ方に優先度を持たせたい場合はここを工夫する !!!
                    if(m_Indeg[v] == 0) { m_Zeros.push(make_pair(777, v)); }
                }
            }
            // m_Ans にトポロジカルソート結果が入る
            return (m_Ans.size() == m_N) ? true : false;
        }

        // 頂点 i の入次数を得る O(1)
        int GetIndeg(int i) { return static_cast<int>(m_G2[i].size()); }

        // 頂点 i の指し先 vector を取得 O(1)
        const vector<int>& GetForward(int i) { return m_G[i]; }
        // 頂点 i の指され元 vector を取得 O(1)
        const vector<int>& GetBackward(int i) { return m_G2[i]; }

        // トポロジカルソートの結果を得る O(V)
        // ret に結果が入る。TopSort 後でないと無効
        void GetResult(vector<int>& ret) {
            assert(m_Ans.size() == m_N);
            ret.clear();
            copy(m_Ans.begin(), m_Ans.end(), back_inserter(ret));
        }

        // O(V)
        // トポロジカルソート後、各頂点がどの位置になったかの逆引き vector を得る
        // ret[i] = val → 頂点 i はソート後位置 val になる
        // ret に結果が入る。TopSort 後でないと無効
        void GetPos(vector<int>& ret) {
            assert(m_Ans.size() == m_N);
            ret.resize(m_N);
            for(int i = 0; i < m_N; ++i) { ret[m_Ans[i]] = i; }
        }

    private:
        // 入力必須
        int                                 m_N;        // 頂点数
        vector< vector<int> >               m_G;        // グラフの隣接リスト
        vector< vector<int> >               m_G2;       // m_G のエッジ向きが逆方向の隣接リスト
        // 一時利用
        vector<int>                         m_Indeg;    // 各頂点の入次数(TopSort後使い捨てられる)
        priority_queue< pair<int, int> >    m_Zeros;    // 入次数 0 の頂点の集合(TopSort後使い捨てられる)
        // 答え
        vector<int>                         m_Ans;      // ソート結果。ソート後のノード id が入る
};

// Tarjan のアルゴリズム O(E + V)
// 頂点 id の基数は 0 とする
class Tarjan {
    public:
        // n : 頂点数
        Tarjan(int n) { Init(n); }
        Tarjan() {}
        void Init(int n) {
            m_N = n;
            m_G.assign(m_N, vector<int>());
            m_G2.assign(m_N, vector<int>());
            m_IsUsed.assign(m_N, false);
            m_Ans.clear();
        }

        // 頂点 from から頂点 to へ行く辺を追加
        void AddEdge(int from, int to) {
            m_G[from].push_back(to);
            m_G2[to].push_back(from);
        }

        // トポロジカルソート O(E + V)
        void Visit (int u) {
            if(m_IsUsed[u]) { return; }
            m_IsUsed[u] = true;
            for(auto& v : m_G[u]) { Visit(v); }
            m_Ans.push_back(u);
        }
        void TopSort() {
            for(int u = 0; u < m_N; ++u) { Visit(u); }
            reverse(m_Ans.begin(), m_Ans.end());
            // m_Ans にトポロジカルソート結果が入る
        }

        // 頂点 i の入次数を得る O(1)
        int GetIndeg(int i) { return static_cast<int>(m_G2[i].size()); }

        // 頂点 i の指し先 vector を取得 O(1)
        const vector<int>& GetForward(int i) { return m_G[i]; }
        // 頂点 i の指され元 vector を取得 O(1)
        const vector<int>& GetBackward(int i) { return m_G2[i]; }

        // トポロジカルソートの結果を得る O(V)
        // ret に結果が入る。TopSort 後でないと無効
        void GetResult(vector<int>& ret) {
            assert(m_Ans.size() == m_N);
            ret.clear();
            copy(m_Ans.begin(), m_Ans.end(), back_inserter(ret));
        }

        // O(V)
        // トポロジカルソート後、各頂点がどの位置になったかの逆引き vector を得る
        // ret[i] = val → 頂点 i はソート後位置 val になる
        // ret に結果が入る。TopSort 後でないと無効
        void GetPos(vector<int>& ret) {
            assert(m_Ans.size() == m_N);
            ret.resize(m_N);
            for(int i = 0; i < m_N; ++i) { ret[m_Ans[i]] = i; }
        }
    private:
        // 入力必須
        int                     m_N;        // 頂点数
        vector< vector<int> >   m_G;        // グラフの隣接リスト
        vector< vector<int> >   m_G2;       // m_G のエッジ向きが逆方向の隣接リスト
        // 一時利用
        vector<bool>            m_IsUsed;   // 探索済みなら true
        // 答え
        vector<int>             m_Ans;      // ソート結果。ソート後のノード id が入る
};

int N, M;
#ifdef USE_TARJAN
Tarjan dag;
#else
Kahn dag;
#endif

vector<int> pos;             // ソート後の位置

signed main()
{
    cin >> N >> M;
    dag.Init(N);
    REP(i, N + M - 1) {
        int a, b;
        cin >> a >> b;
        a--; b--;
        dag.AddEdge(a, b);
    }
    dag.TopSort();

    pos.assign(N, 0);
    dag.GetPos(pos);

    REP(i, N) {
        // 入次数が 2 以上なら 1 まで減らす
        if(dag.GetIndeg(i) >= 2) {
            int val = -1;
            int ind = -1;
            for(auto& cand : dag.GetBackward(i)) {
                // 一番近いやつを残す
                if(pos[cand] > val) {
                    ind = cand;
                    val = pos[cand];
                }
            }
            assert(ind >= 0);
            printf("%lld\n", ind + 1);
        }
        else if(dag.GetIndeg(i) == 0) {
            printf("0\n");
        }
        else {
            printf("%lld\n", dag.GetBackward(i)[0] + 1);
        }
    }

    return 0;
}


Measured memory_bytes: 15352