/****************************************************************************
 *  Kompilacja:  javac UF.java
 *  Wykonanie:  java UF < input.txt
 *  Zależności: StdIn.java StdOut.java
 *
 *  Szybka metoda Union z wagami (bez kompresji ścieżki).
 *
 *  % java UF < tinyUF.txt
 *  4 3
 *  3 8
 *  6 5
 *  9 4
 *  2 1
 *  5 0
 *  7 2
 *  6 1
 *  Liczba składowych: 2
 *
 ****************************************************************************/


/**
 *  Klasa <tt>UF</tt> reprezentuje strukturę danych union-find.
 *  Udostępnia operacje <em>union</em> i <em>find</em>,
 *  a także metodę do określania liczby rozłącznych
 *  zbiorów.
 *  <p>
 *  W tej implementacji wykorzystano ważoną szybką metodę Union.
 *  Czas tworzenia struktury danych o N obiektach rośnie liniowo.
 *  Później wszystkie operacje w najgorszym razie wymagają czasu logarytmicznego.
 *  <p>
 *  Dodatkową dokumentację znajdziesz w <a href="http://algs4.cs.princeton.edu/15uf">podrozdziale 1.5</a> książki
 *  <i>Algorytmy, wydanie czwarte</i> Roberta Sedgewicka i Kevina Wayne'a.
 */

public class UF {
    private int[] id;    // id[i] = rodzic i
    private int[] sz;    // sz[i] = liczba obiektów w poddrzewie o korzeniu w i
    private int count;   // Liczba składowych

   /**
     * Tworzenie pustej struktury danych Union-Find za pomocą N izolowanych zbiorów.
     */
    public UF(int N) {
        count = N;
        id = new int[N];
        sz = new int[N];
        for (int i = 0; i < N; i++) {
            id[i] = i;
            sz[i] = 1;
        }
    }

   /**
     * Zwracanie identyfikatora komponentu odpowiadającego obiektowi p.
     */
    public int find(int p) {
        while (p != id[p])
            p = id[p];
        return p;
    }

   /**
     * zwracanie liczby rozłącznych zbiorów.
     */
    public int count() {
        return count;
    }

  
   /**
     * Czy obiekty p i q znajdują się w tym samym zbiorze?
     */
    public boolean connected(int p, int q) {
        return find(p) == find(q);
    }

  
   /**
     * Zastępowanie zbiorów zawierających p i q ich sumą.
     */
    public void union(int p, int q) {
        int i = find(p);
        int j = find(q);
        if (i == j) return;

        // Ustawianie mniejszego korzenia, tak aby prowadził do większego
        if   (sz[i] < sz[j]) { id[i] = j; sz[j] += sz[i]; }
        else                 { id[j] = i; sz[i] += sz[j]; }
        count--;
    }


    public static void main(String[] args) {
        int N = StdIn.readInt();
        UF uf = new UF(N);

        // Wczytywanie ciągu par liczb całkowitych (każda z przedziału od 0 do N-1),
        // i wywołanie dla każdej pary metody find(). Jeśli elementy z pary nie są połączone, 
        // należy wywołać union() i wyświetlić parę.
        while (!StdIn.isEmpty()) {
            int p = StdIn.readInt();
            int q = StdIn.readInt();
            if (uf.connected(p, q)) continue;
            uf.union(p, q);
            StdOut.println(p + " " + q);
        }
        StdOut.println("Liczba składowych: " + uf.count());
    }

}

