#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

// Kahn のアルゴリズム
// 頂点 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_St = stack<int>();
    }

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

    // トポロジカルソートに成功したら true
    // グラフが DAG でない場合は false
    bool TopSort() {
        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_St.push(i); }
        }
        while(m_St.size() > 0) {
            int u = m_St.top(); m_St.pop();
            m_KahnAns.push_back(u);      // 入次数 0 の頂点を結果列に挿入
            for(auto& v : m_G[u]) {
                m_Indeg[v]--;
                if(m_Indeg[v] == 0) { m_St.push(v); }
            }
        }
        // m_KahnAns にトポロジカルソート結果が入る
        return (m_KahnAns.size() == m_N) ? true : false;
    }

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

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

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

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

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

ll N, M;
Kahn kahn;

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

signed main()
{
    cin >> N >> M;
    kahn.Init(N);
    REP(i, N + M - 1) {
        ll a, b;
        cin >> a >> b;
        a--; b--;
        kahn.AddEdge(a, b);
    }
    bool ret = kahn.TopSort();
    assert(ret);

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

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

    return 0;
}


Measured memory_bytes: 14200