#include #include #include #include #include #include #include using namespace std; typedef vector vi; struct Edge { int to, h, i; bool tohas1; }; const int MAXN = 100 * 1000; int n, m; Edge E[2*MAXN]; vi G[MAXN]; // Can I put 1 at vertex, and similarly X at vertex? bool can1[MAXN], canX[MAXN]; void cansolve(int u, int v, int x) { canX[v] = can1[v] = true; int sel1 = -1; unordered_map seen; for (int i : G[v]) { int w = E[i].to; if (u == w) continue; cansolve(v, w, E[i].h); // placing 1 at w, means there is a value E[i].h at v. can1[v] &= can1[w] && seen.find(E[i].h) == seen.end(); if (E[i].h == x) { // we must use the 1 here if (sel1 >= 0) canX[v] = false; if (canX[w]) sel1 = w; else canX[v] = false; } else if (seen.find(E[i].h) != seen.end()) { int wp = E[seen[E[i].h]].to; if (sel1 >= 0) canX[v] = false; if (canX[w] && can1[wp]) sel1 = w; else if (canX[wp] && can1[w]) sel1 = wp; else canX[v] = false; } seen[E[i].h] = i; } if (!canX[v]) return; for (int i : G[v]) { int w = E[i].to; if (u == w || w == sel1) continue; if (!can1[w]) { if (sel1 >= 0) { canX[v] = false; return; } sel1 = i; } } } void solve1(int u, int v); void solveX(int u, int v); void solve1(int u, int v) { assert(can1[v]); for (int i : G[v]) { int w = E[i].to; if (u == w) continue; assert(can1[w]); E[i].tohas1 = true; solve1(v, w); } } void solveX(int u, int v, int x) { int sel1 = -1; unordered_map seen; assert(canX[v]); for (int i : G[v]) { int w = E[i].to; if (u == w) continue; if (E[i].h == x) { // we must use the 1 here assert(sel1 < 0 && canX[w]); sel1 = w; } else if (seen.find(E[i].h) != seen.end()) { int wp = E[seen[E[i].h]].to; assert(sel1 < 0); if (canX[w] && can1[wp]) { sel1 = w; } else { assert(canX[wp] && can1[w]); sel1 = wp; } } seen[E[i].h] = i; } for (int i : G[v]) { int w = E[i].to; if (u == w || w == sel1) continue; if (!can1[w]) { assert(sel1 < 0); sel1 = i; } } for (int i : G[v]) { int w = E[i].to; if (u == w) continue; if (w == sel1) { E[i^1].tohas1 = true; solveX(v, w, E[i].h); } else { E[i].tohas1 = true; solve1(v, w); } } } bool solve_tree() { cansolve(0, 0, -1); if (!canX[0]) return false; solveX(0, 0, -1); return true; } // returns cycle in a connected undirected graph, if exists // note: it returns the indices of the EDGES in E. vi find_cycle(int v0 = 0) { vi p(n, -1), h(n, 0), s{v0}; h[v0] = 1; while (!s.empty()) { int v = s.back(); s.pop_back(); for (int i : G[v]) { int w = E[i].to; if (!h[w]) s.push_back(w), p[w] = i^1, h[w] = h[v] + 1; else if (i != p[v]) { deque cyc{v}; while (v != w) if (h[v] > h[w]) cyc.push_back(v = E[p[v]].to); else cyc.push_front(w), w = E[p[w]].to; return vi(begin(cyc), end(cyc)); } } } return {}; } bool solve_rho() { // 1. Find the cycle bool isok; vi cyc = find_cycle(); int ncyc = (int) cyc.size(); assert(ncyc >= 2); unordered_set seen; if (ncyc == 2) { // edge case, ugly! int e0 = -1, e1 = -1; for (int i : G[cyc[0]]) if (E[i].to == cyc[1]) (e0 < 0 ? e0 : e1) = i; // 2. First assign e0's 1 to cyc[1] and e1's 1 to cyc[0]. isok = true; seen = { E[e0].h }; for (int i : G[cyc[1]]) { if (E[i].to == cyc[0]) continue; cansolve(cyc[1], E[i].to, -1); isok &= seen.find(E[i].h) == seen.end() && can1[E[i].to]; seen.insert(E[i].h); } seen = { E[e1].h }; for (int i : G[cyc[0]]) { if (E[i].to == cyc[1]) continue; cansolve(cyc[0], E[i].to, -1); isok &= seen.find(E[i].h) == seen.end() && can1[E[i].to]; seen.insert(E[i].h); } if (isok) { E[e0^1].tohas1 = E[e1].tohas1 = true; for (int v : cyc) for (int i : G[v]) { if (E[i].to != cyc[0] && E[i].to != cyc[1]) E[i].tohas1 = true, solve1(v, E[i].to); } return true; } // 3. Second assign e0's 1 to cyc[0] and e1's 1 to cyc[1]. isok = true; seen = { E[e0].h }; for (int i : G[cyc[0]]) { if (E[i].to == cyc[1]) continue; cansolve(cyc[0], E[i].to, -1); isok &= seen.find(E[i].h) == seen.end() && can1[E[i].to]; seen.insert(E[i].h); } seen = { E[e1].h }; for (int i : G[cyc[1]]) { if (E[i].to == cyc[0]) continue; cansolve(cyc[1], E[i].to, -1); isok &= seen.find(E[i].h) == seen.end() && can1[E[i].to]; seen.insert(E[i].h); } if (isok) { E[e0].tohas1 = E[e1^1].tohas1 = true; for (int v : cyc) for (int i : G[v]) { if (E[i].to != cyc[0] && E[i].to != cyc[1]) E[i].tohas1 = true, solve1(v, E[i].to); } return true; } return false; } // 2. Try to impose all one way. isok = true; for (int k = 0, j = ncyc - 1, i = j - 1; isok && k < ncyc; i = j, j = k++) { // j (x) -> i(1) int x = -1; for (int ei : G[cyc[j]]) { if (E[ei].to == cyc[i]) { x = E[ei].h; break; } } assert(x != -1); seen = { x }; for (int ei : G[cyc[j]]) { if (E[ei].to == cyc[i] || E[ei].to == cyc[k]) continue; cansolve(cyc[j], E[ei].to, -1); isok &= seen.find(E[ei].h) == seen.end() && can1[E[ei].to]; seen.insert(E[ei].h); } } if (isok) { for (int k = 0, j = ncyc - 1, i = j - 1; isok && k < ncyc; i = j, j = k++) { // j (x) -> i(1) for (int ei : G[cyc[j]]) { if (E[ei].to == cyc[k]) continue; E[ei].tohas1 = true; if (E[ei].to != cyc[i]) solve1(cyc[j], E[ei].to); } } return true; } // 3. Try to impose the conditions the other way. isok = true; for (int i = 0, j = ncyc - 1, k = j - 1; isok && i < ncyc; k = j, j = i++) { // j (x) -> i(1) int x = -1; for (int ei : G[cyc[j]]) { if (E[ei].to == cyc[i]) { x = E[ei].h; break; } } assert(x != -1); seen = { x }; for (int ei : G[cyc[j]]) { if (E[ei].to == cyc[i] || E[ei].to == cyc[k]) continue; cansolve(cyc[j], E[ei].to, -1); isok &= seen.find(E[ei].h) == seen.end() && can1[E[ei].to]; seen.insert(E[ei].h); } } if (isok) { for (int i = 0, j = ncyc - 1, k = j - 1; isok && i < ncyc; k = j, j = i++) { // j (x) -> i(1) for (int ei : G[cyc[j]]) { if (E[ei].to == cyc[k]) continue; E[ei].tohas1 = true; if (E[ei].to != cyc[i]) solve1(cyc[j], E[ei].to); } } return true; } return false; } int main() { cin >> n; m = n; assert(m >= n - 1); if (m > n) { cout << "impossible" << endl; return 0; } for (int i = 0; i < m; i++) { int u, v, h; cin >> u >> v >> h; --u; --v; E[2*i] = Edge{ v, h, i, false }; G[u].push_back(2*i); E[2*i + 1] = Edge{ u, h, i, false }; G[v].push_back(2*i + 1); } bool res = m == n ? solve_rho() : solve_tree(); if (res) { for (int i = 0; i < m; i++) { if (E[2*i].tohas1 && E[2*i+1].tohas1) cout << "???" << endl; else if (E[2*i].tohas1) cout << (1 + E[2*i].to) << endl; else cout << (1 + E[2*i+1].to) << endl; } for (int i = 0; i < m; i++) assert(E[2*i].tohas1 != E[2*i+1].tohas1); } else { cout << "impossible" << endl; } return 0; }