import std.algorithm; import std.array; import std.conv; import std.math; import std.range; import std.stdio; import std.string; import std.typecons; int readint() { return readln.chomp.to!int; } int[] readints() { return readln.split.to!(int[]); } alias Pair = Tuple!(int, "a", int, "b"); Pair readpair() { auto xs = readints; return Pair(xs[0], xs[1]); } void main() { auto nmq = readints; int n = nmq[0], m = nmq[1], q = nmq[2]; Pair[] pairs; for (int i = 0; i < m; i++) { pairs ~= readpair(); } bool[Pair] breakMap; Pair[] breaks; for (int i = 0; i < q; i++) { auto p = readpair(); breaks ~= p; breakMap[p] = true; } auto uf = new UnionFind(n); foreach (p; pairs) { // 破壊に関係ない橋を予め接続しておく if (p !in breakMap) { uf.unite(p.a, p.b); } } auto ans = new int[](n + 1); ans[] = -1; // 全て到達できる foreach_reverse (i, brk; breaks) { const rootA = uf.root(brk.a); const rootB = uf.root(brk.b); const root1 = uf.root(1); if (rootA != rootB && (rootA == root1 || rootB == root1)) { int nth = cast(int) i + 1; int node = rootA == root1 ? rootB : rootA; // 1 と接続された集合のルート /* foreach (x; uf.group(node)) { ans[x] = nth; }*/ uf.doEach(node, delegate(e) { ans[e] = nth; }); } uf.unite(brk.a, brk.b); } for (int i = 2; i <= n; i++) { if (uf.isSame(1, i)) writeln(ans[i]); else writeln(0); // 最初から到達できない } } class SList { public int node; public SList next; public SList tail; this(int node) { this.node = node; this.next = null; this.tail = this; } /// 引数 list を破壊的に変更する void append(SList list) { this.tail.next = list; this.tail = list.tail; } int[] toArray() { int[] buf = [node]; auto p = this.next; while (p !is null) { buf ~= p.node; p = p.next; } return buf; } } class UnionFind { private int[] _data; private int[] _nexts; // 次の要素。なければ -1 private int[] _tails; // 末尾の要素 this(int n) { _data = new int[](n + 1); _nexts = new int[](n + 1); _tails = new int[](n + 1); _data[] = -1; _nexts[] = -1; for (int i = 0; i < _tails.length; i++) _tails[i] = i; } int root(int a) { if (_data[a] < 0) return a; return _data[a] = root(_data[a]); } bool unite(int a, int b) { int ra = root(a); int rb = root(b); if (ra == rb) return false; // ra に rb を繋げる _data[ra] += _data[rb]; _data[rb] = ra; // ra の末尾の後ろに rb を繋げる _nexts[_tails[ra]] = rb; // ra の末尾が rb の末尾になる _tails[ra] = _tails[rb]; return true; } bool isSame(int a, int b) { return root(a) == root(b); } /// a が属する集合のサイズ int groupSize(int a) { return -_data[root(a)]; } void doEach(int a, void delegate(int) fn) { auto node = root(a); while (node != -1) { fn(node); node = _nexts[node]; } } }