#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 0
#  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) : m_N(n) {}
    Kahn() {}
    void Init(int n) { m_N = n; }

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

    // トポロジカルソートに成功したら true
    // グラフが DAG でない場合は false
    bool TopologicalSort() {
        // 各頂点の入次数を計算 O(E)
        for(int i = 0; i < m_N; ++i) {
            for(int j = 0; j < m_G[i].size(); ++j) {
                m_Indeg[m_G[i][j]]++;
            }
        }
        // 入次数 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;
    }

    void MakePos(vector<ll>& v) {
        REP(i, m_N) {
            DBG("%lld ", m_KahnAns[i]);
            v[m_KahnAns[i]] = i;
        }
        DBG("\n");
    }

private:
    int         m_N;                // N: 頂点数
    vector<int> m_G[MAX_N];         // ind から val に向かって延びている辺の隣接リスト
    int         m_Indeg[MAX_N];     // 各頂点の入次数
    stack<int>  m_St;               // 入次数 0 の頂点の集合
    vector<int> m_KahnAns;
};

ll N, M;
Kahn* k1;

vector<ll> origin[MAX_N];   // ノード i に入ってくるエッジの指し元
vector<ll> indeg;           // にゅうじすう

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

bool dfs(ll goal, ll cur) {
    if(cur == goal) { return true; }
    for(auto& nxt : origin[cur]) {
        if(dfs(goal, nxt)) { return true; }
    }
    return false;
}

// 辺 from, to を使わなくても cand が child の祖先なら true
bool isParent(ll child, ll cand) {
    for(auto& nxt : origin[child]) {
        if(nxt == cand) { continue; }
        if(dfs(cand, nxt)) { return true; }
    }
    return false;
}

signed main()
{
    cin >> N >> M;
    Kahn* k1 = new Kahn(N);
    REP(i, N + M - 1) {
        ll a, b;
        cin >> a >> b;
        a--; b--;
        k1->AddEdge(a, b);

        origin[b].push_back(a);
    }
    bool ret = k1->TopologicalSort();
    assert(ret);

    pos.assign(N, 0);
    k1->MakePos(pos);

    REP(i, N) {
        // 入じ数が 2 以上なら 1 まで減らす
        if(origin[i].size() >= 2) {
            ll val = -1;
            ll ind = -1;
            for(auto& cand : origin[i]) {
                // この辺を消すと親子関係じゃなくなるなら、残す
                //if(!isParent(i, cand)) {
                //    printf("%lld\n", cand + 1);
                //    break;
                //}

                // 一番近いやつを残す
                if(pos[cand] > val) {
                    ind = cand;
                    val = pos[cand];
                }
            }
            assert(ind >= 0);
            printf("%lld\n", ind + 1);
        }
        else if(origin[i].size() == 0) {
            printf("0\n");
        }
        else {
            printf("%lld\n", origin[i][0] + 1);
        }
    }



    return 0;
}


Measured memory_bytes: 14200