Problem:
Score : 500 points

Problem Statement

We have N locked treasure boxes, numbered 1 to N.

A shop sells M keys. The i-th key is sold for a_i yen (the currency of Japan), and it can unlock b_i of the boxes: Box c_{i1}, c_{i2}, ..., c_{i{b_i}}. Each key purchased can be used any number of times.

Find the minimum cost required to unlock all the treasure boxes. If it is impossible to unlock all of them, print -1.

Constraints

All values in input are integers.

1 \leq N \leq 12

1 \leq M \leq 10^3

1 \leq a_i \leq 10^5

1 \leq b_i \leq N

1 \leq c_{i1} < c_{i2} < ... < c_{i{b_i}} \leq N

Input

Input is given from Standard Input in the following format:

N M
a_1 b_1
c_{11} c_{12} ... c_{1{b_1}}
:
a_M b_M
c_{M1} c_{M2} ... c_{M{b_M}}

Output

Print the minimum cost required to unlock all the treasure boxes.
If it is impossible to unlock all of them, print -1.

Sample Input 1

2 3
10 1
1
15 1
2
30 2
1 2

Sample Output 1

25

We can unlock all the boxes by purchasing the first and second keys, at the cost of 25 yen, which is the minimum cost required.

Sample Input 2

12 1
100000 1
2

Sample Output 2

-1

We cannot unlock all the boxes.

Sample Input 3

4 6
67786 3
1 3 4
3497 1
2
44908 3
2 3 4
2156 3
2 3 4
26230 1
2
86918 1
3

Sample Output 3

69942

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

// optional
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;

public static class Program {
    public static void Main() {
        #region SetAutoFlushIsFalse
#if !DEBUG
        var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
        Console.SetOut(sw);
#endif
        #endregion

        var nm = ReadLib.ReadAsIntArray();

        var n = nm[0];
        var m = nm[1];

        var costs = new int[m + 1];
        var opens = new int[m + 1];

        for (int i = 1; i <= m; i++) {
            var ab = ReadLib.ReadAsIntArray();
            costs[i] = ab[0];

            var cs = ReadLib.ReadAsIntArray();
            opens[i] = cs.Select(c => 1 << (c - 1)).Sum();
        }

        // 2次元目: 2進数のn桁目（配列として見たときの**n-1**番目）は宝箱の開閉を示す
        var dp = new int[m + 1, 1 << n];
        dp.Initialize(int.MaxValue);
        dp[0, 0] = 0;

        for (int key = 1; key <= m; key++) {
            for (int open = 0; open < 1 << n; open++) {
                dp[key, open] = Math.Min(dp[key, open], dp[key - 1, open]);

                var afterOpen = open | opens[key];
                dp[key, afterOpen] = Math.Min(
                    dp[key, afterOpen],
                    dp[key - 1, open] == int.MaxValue ? int.MaxValue : dp[key - 1, open] + costs[key]   // オーバーフロー対策
                    );
            }
        }

        var score = dp[m, (1 << n) - 1] != int.MaxValue ? dp[m, (1 << n) - 1] : -1;
        Console.WriteLine(score);

        #region Flush
#if !DEBUG
        Console.Out.Flush();
        sw.Close();
#endif
        #endregion
    }
}

static class ReadLib {
    public static long ReadAsLong() {
        return long.Parse(Console.ReadLine());
    }

    public static int ReadAsInt() {
        return int.Parse(Console.ReadLine());
    }

    public static int[] ReadAsIntArray(int step = 0) {
        return Console.ReadLine().Split(' ').Select(s => int.Parse(s) + step).ToArray();
    }

    public static long[] ReadAsLongArray(int step = 0) {
        return Console.ReadLine().Split(' ').Select(s => long.Parse(s) + step).ToArray();
    }

    public static ulong[] ReadAsULongArray() {
        return Console.ReadLine().Split(' ').Select(s => ulong.Parse(s)).ToArray();
    }

    public static string[] ReadAsStringArray() {
        return Console.ReadLine().Split(' ');
    }

    public static int[] ReadNTimesAsInt(int n, int step = 0) {
        return Enumerable.Range(1, n).Select(s => int.Parse(Console.ReadLine()) + step).ToArray();
    }

    public static char[][] ReadNTimesAsCharArray(int n) {
        return Enumerable.Range(1, n).Select(i => Console.ReadLine().ToArray()).ToArray();
    }

    public static long[] ReadNTimesAsLong(int n, int step = 0) {
        return Enumerable.Range(1, n).Select(s => long.Parse(Console.ReadLine()) + step).ToArray();
    }

    public static int[][] ReadNTimesAsIntArray(int n, int step = 0) {
        return Enumerable.Range(1, n).Select(s => ReadAsIntArray(step)).ToArray();
    }

    public static string[] ReadNTimesAsString(int n) {
        return Enumerable.Range(1, n).Select(s => Console.ReadLine()).ToArray();
    }

}

namespace Heaps {
    /// <summary>優先度付きキュー</summary>
    public class PriorityQueue<T> : IEnumerable<T>, IReadOnlyCollection<T> where T : IComparable<T> {
        public List<T> HeapArray { get; }
        public T NIL { get; }
        public bool IsMax { get; }

        public int Count => HeapArray.Count;

        /// <param name="heapArray">ヒープ</param>
        /// <param name="NIL">NILの値</param>
        /// <param name="IsMax">True時：大きい方から取り出す、False時：小さい方から取り出す</param>
        public PriorityQueue(T NIL, IEnumerable<T> heapArray = null, bool IsMax = true) {
            this.HeapArray = heapArray == null ? new List<T>() : heapArray.ToList();
            this.NIL = NIL;
            this.IsMax = IsMax;
            Update();
        }

        /// <summary>KeyのValueを取得</summary>
        public T this[int id] {
            get {
                if (id <= 0 || id > HeapArray.Count) {
                    return NIL;
                }

                return HeapArray[id - 1];
            }
        }

        class Node {
            public int Key { get; }
            public T Value => outer[Key];
            readonly PriorityQueue<T> outer;

            public Node(int key, PriorityQueue<T> outer) {
                Key = key;
                this.outer = outer;
            }

            public Node Left {
                get {
                    var leftKey = 2 * Key;
                    return new Node(leftKey, outer);
                }
            }

            public Node Right {
                get {
                    var rightkey = 2 * Key + 1;
                    return new Node(rightkey, outer);
                }
            }

            public Node Parent {
                get {
                    var parentKey = Key / 2;
                    return new Node(parentKey, outer);
                }
            }
        }

        private Node GetNode(int key) {
            return new Node(key, this);
        }

        /// <summary>max Heap木とする O(N)</summary>
        public void Update() {
            var n = HeapArray.Count() / 2;
            /// <summary>葉から順に処理を行う</summary>
            for (int i = n; i >= 1; i--) {
                Heapify(i);
            }
        }

        /// <summary>idを根とする部分木について、Heap木を維持したまま、idを下降させる</summary>
        private void Heapify(int key) {
            var current = GetNode(key);

            var left = current.Left;
            var right = current.Right;

            var largest = current;
            var sign = IsMax ? 1 : -1;
            if (left.Value.CompareTo(NIL) != 0 && left.Value.CompareTo(current.Value) * sign > 0) {
                largest = left;
            }

            if (right.Value.CompareTo(NIL) != 0 && right.Value.CompareTo(largest.Value) * sign > 0) {
                largest = right;
            }

            if (largest.Key != key) {
                HeapArray.Swap(key - 1, largest.Key - 1);   // 元と異なるときはSwapする
                Heapify(largest.Key); // largest.Key は元のkeyの場所。葉で更にMaxHeaptyを行う。
            }
        }

        /// <summary>keyの値を挿入する</summary>
        public void Enqueue(T value) {
            HeapArray.Add(value);

            var inserted = GetNode(HeapArray.Count);    // 末尾に挿入
            var parent = inserted.Parent;
            var sign = IsMax ? 1 : -1;

            // 挿入Nodeが根になる or 親 > 子になるまで親とNodeを交換
            while (inserted.Key > 1 && value.CompareTo(parent.Value) * sign > 0) {
                HeapArray.Swap(inserted.Key - 1, parent.Key - 1);
                inserted = parent;
                parent = inserted.Parent;
            }
        }

        public T Dequeue() {
            if (HeapArray.Count == 0) {
                return NIL;
            }

            var popVal = HeapArray.First();
            var newRootVal = HeapArray.Last();  // 新しい根となる。

            HeapArray.RemoveAt(HeapArray.Count - 1);    // del last

            if (HeapArray.Count > 0) {
                HeapArray[0] = newRootVal;
                Heapify(1);
            }

            return popVal;
        }

        public T Peek() {
            return HeapArray.First();
        }

        public IEnumerator<T> GetEnumerator() {
            while (HeapArray.Count > 0) {
                yield return Dequeue();
            }
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }

        public void Clear() {
            HeapArray.Clear();
        }

        public bool Contains(T val) {
            return HeapArray.Contains(val);
        }

        public PriorityQueue<T> Clone() {
            return new PriorityQueue<T>(NIL, HeapArray.DeepCopy());
        }
    }
}

/// <summary>拡張メソッドs</summary>
static class MyExtention {
    /// <summary>iとjを入れ替える</summary>
    public static void Swap<T>(this T[] arr, int i, int j) {
        var buf = arr[i];
        arr[i] = arr[j];
        arr[j] = buf;
    }

    public static void Swap<T>(this List<T> list, int i, int j) {
        var buf = list[i];
        list[i] = list[j];
        list[j] = buf;
    }

    /// <summary>
    /// partition。末尾sArr[r]を基準として、左に基準より小さい数、右に基準より大きい数が存在するように配列を並び替える。
    /// </summary>
    /// <param name="sArr"></param>
    /// <param name="p">partitionの先頭の添字</param>
    /// <param name="r">partitionの末尾の添字</param>
    /// <returns>partitionの基準が存在する添字</returns>
    public static int Partition(this int[] sArr, int p, int r) {
        var x = sArr[r];
        var i = p - 1;
        for (int j = p; j < r; j++) {
            if (sArr[j] <= x) {
                i++;
                sArr.Swap(i, j);
            }
        }
        sArr.Swap(i + 1, r);
        return i + 1;
    }

    /// <summary>深いコピーをする</summary>
    public static T DeepCopy<T>(this T obj) {
        T result;
        var b = new BinaryFormatter();
        using (var mem = new MemoryStream()) {
            b.Serialize(mem, obj); //  binaryFormatterによりobjをmemにserial化して書き出す
            mem.Position = 0; // memのpotisionを先頭に戻す
            result = (T)b.Deserialize(mem); // memの内容を読み取る。
        }
        return result;
    }

    public static string Left(this string str, int length) {
        return str.Substring(0, length);
    }

    public static string Right(this string str, int length) {
        return str.Substring(str.Length - length, length);
    }

    public static int Find<T>(this T[] array, T obj) {
        return Array.IndexOf(array, obj);
    }

    public static int LastFind<T>(this T[] array, T obj) {
        return Array.LastIndexOf(array, obj);
    }

    /// <summary>同一の値で埋める</summary>
    public static void Initialize<T>(this T[] array, T value) {
        for (int i = 0; i < array.Length; i++) {
            array[i] = value;
        }
    }

    /// <summary>同一の値で埋める</summary>
    public static void Initialize<T>(this T[,] array, T value) {
        for (int i = 0; i < array.GetLength(0); i++) {
            for (int j = 0; j < array.GetLength(1); j++) {
                array[i, j] = value;
            }
        }
    }
}

static class MyMath {
    /// <summary>階乗</summary>
    public static long Factorial(int n) {
        if (n == 0) {
            return 1;
        }

        return Permutation(n, 0);
    }

    public static long Permutation(int n, int k) {
        return Enumerable.Range(0, n - k).Select(i => n - i).Aggregate(1, (b, i) => b * i);
    }

    #region FullPower
    /// <summary>総乗、π</summary>
    public static long FullPower(IEnumerable<long> vs) {
        return vs.Aggregate(1L, (a, b) => a * b);
    }

    public static long FullPower(IEnumerable<int> vs) {
        return vs.Aggregate(1L, (a, b) => a * b);
    }
    #endregion

    /// <summary>最大公約数</summary>
    public static long Gcd(long item1, long item2) {
        var a = Math.Max(item1, item2);
        var b = Math.Min(item1, item2);

        while (true) {
            long mod;
            Math.DivRem(a, b, out mod);
            if (mod == 0) {
                break;
            }

            a = b;
            b = mod;
        }
        return b;
    }

    /// <summary>約分。最大公約数で割ったものを返す。</summary>
    public static Tuple<long, long> DeviedByGcd(long item1, long item2) {
        var gcd = Gcd(item1, item2);
        return new Tuple<long, long>(item1 / gcd, item2 / gcd);
    }

    public static long Lcm(int item1, int item2) {
        return Math.BigMul(item1, item2) / Gcd(item1, item2);
    }

    public static ModCombination ModCombination(int maxN, int mod = 1000000007) {
        return new ModCombination(maxN, mod);
    }

    /// <summary>a^n mod</summary>
    public static int ModPow(long a, long n, int mod = 1000000007) {
        long res = 1;
        long pow = a;

        while (n > 0) {
            if ((n & 1) == 1) {
                res = (pow * res) % mod;    // resにaをかける
            }

            pow = (pow * pow) % mod;    // 次のaはa^2
            n >>= 1;
        }

        return (int)res;
    }

    /// <summary>約数を列挙</summary>
    public static long[] Divisors(long n) {
        var ret = new List<long>();
        var sqrt = Math.Ceiling(Math.Sqrt(n));
        for (long i = 1; i <= sqrt; i++) {
            if (i * i > n) {
                continue;
            }

            if (n % i != 0) {
                continue;
            }

            ret.Add(i);

            if (n / i == i) {
                continue;
            }

            ret.Add(n / i);
        }

        return ret.ToArray();
    }
}

static class MyMethod {
    /// <summary>数え上げて、key, 出現回数に変換</summary>
    public static Dictionary<T, int> CountUp<T>(IEnumerable<T> str) {
        return
            str
            .GroupBy(c => c)
            .Select(g => new KeyValuePair<T, int>(g.Key, g.Count()))
            .ToDictionary(e => e.Key, e => e.Value);
    }

    /// <summary>ある要素を持つindexをすべて返す</summary>
    public static int[] InducesOfValue<T>(this T[] arr, T searchValue) {
        return arr
            .Select((v, i) => new { v, i })
            .Where(o => searchValue.Equals(o.v))
            .Select(o => o.i)
            .ToArray();
    }

    /// <summary>ある要素を持つindexをすべて返す</summary>
    public static int[] Induces<T>(this IEnumerable<T> arr, T searchValue) {
        return arr.ToArray().InducesOfValue(searchValue);
    }
}

/// <summary>組み合わせ</summary>
class ModCombination {
    private int mod;
    private int[] fac;
    private int[] finv;
    private int[] inv;

    public ModCombination(int max, int mod = 1000000007) {
        this.mod = mod;
        fac = new int[max]; // modが法のn!の列

        inv = new int[max]; // indexの逆元の列
        finv = new int[max]; // modを法とした、invの累積の累積

        fac[0] = 1; fac[1] = 1;
        finv[0] = 1; finv[1] = 1;
        inv[1] = 1;

        for (int i = 2; i < max; i++) {
            fac[i] = (int)(Math.BigMul(fac[i - 1], i) % mod);  // factorを求める
            var s = Math.BigMul(inv[mod % i], mod / i) % mod;
            inv[i] = (int)(mod - s);　 // iに対する逆元を求める
            finv[i] = (int)(Math.BigMul(finv[i - 1], inv[i]) % mod);
        }
    }
    /// <summary>nCk</summary>
    public int Calculation(int n, int k) {
        if (n < k) return 0;
        if (n < 0 || k < 0) return 0;

        return (int)(Math.BigMul(fac[n], (int)(Math.BigMul(finv[k], finv[n - k]) % mod)) % mod);
    }
}

/// <summary>あまりを計算するためのintのラッパー</summary>
struct ModInt {
    int Value { get; }
    int Mod { get; }

    public override string ToString() {
        return Value.ToString();
    }

    public static explicit operator ModInt(long value) {
        return new ModInt(value);
    }

    public ModInt(long value, int mod = 1000000007) {
        this.Value = (int)(value % mod);
        this.Mod = mod;
    }

    public static explicit operator int(ModInt modInt) {
        return modInt.Value;
    }

    private static void CheckNotEqualMod(ModInt item1, ModInt item2) {
        if (item1.Mod != item2.Mod) {
            throw new Exception("mod not equal");
        }
    }

    public static ModInt operator +(ModInt item1, ModInt item2) {
        CheckNotEqualMod(item1, item2);
        long value = item1.Value + item2.Value;
        return new ModInt(value, item1.Mod);
    }

    public static ModInt operator *(ModInt item1, ModInt item2) {
        CheckNotEqualMod(item1, item2);
        long value = Math.BigMul(item1.Value, item2.Value);
        return new ModInt(value, item1.Mod);
    }

    public static ModInt operator -(ModInt item1, ModInt item2) {
        CheckNotEqualMod(item1, item2);
        long value = item1.Value - item2.Value;
        if (value < 0) {
            value += item1.Mod;
        }
        return new ModInt(value, item1.Mod);
    }

    public static ModInt operator +(ModInt item1, int item2) {
        return item1 + new ModInt(item2, item1.Mod);
    }

    public static ModInt operator *(ModInt item1, int item2) {
        return item1 * new ModInt(item2, item1.Mod);
    }

    public static ModInt operator -(ModInt item1, int item2) {
        return item1 - new ModInt(item2, item1.Mod);
    }


    public override int GetHashCode() {
        return this.Value;
    }

    public override bool Equals(object obj) {
        if (obj == null || this.GetType() != obj.GetType()) {
            return false;
        }

        var modIntObj = (ModInt)obj;
        return modIntObj.Value == this.Value;
    }

    public static bool operator ==(ModInt item1, ModInt item2) {
        return item1.Value == item2.Value;
    }

    public static bool operator !=(ModInt item1, ModInt item2) {
        return !(item1 == item2);
    }

    public static bool operator ==(ModInt item1, int item2) {
        return item1.Value == item2;
    }

    public static bool operator !=(ModInt item1, int item2) {
        return !(item1.Value == item2);
    }

    public static bool operator >=(ModInt item1, ModInt item2) {
        return item1.Value >= item2.Value;
    }

    public static bool operator <=(ModInt item1, ModInt item2) {
        return item1.Value <= item2.Value;
    }

    public static bool operator >=(ModInt item1, int item2) {
        return item1.Value >= item2;
    }

    public static bool operator <=(ModInt item1, int item2) {
        return item1.Value <= item2;
    }

    public static bool operator >(ModInt item1, ModInt item2) {
        return item1.Value > item2.Value;
    }

    public static bool operator <(ModInt item1, ModInt item2) {
        return item1.Value < item2.Value;
    }

    public static bool operator >(ModInt item1, int item2) {
        return item1.Value >= item2;
    }

    public static bool operator <(ModInt item1, int item2) {
        return item1.Value <= item2;
    }
}

// optional
namespace Graph {
    /// <summary>無向グラフ</summary>
    class UndirectedGraph {
        public Dictionary<int, List<int>> Graph { get; }

        /// <param name="graphArr">point1 to point2</param>
        /// <param name="n">グラフの全要素数</param>
        public UndirectedGraph(int[][] graphArr, int n) {
            Graph = new Dictionary<int, List<int>>();

            for (int i = 1; i <= n; i++) {
                Graph.Add(i, new List<int>());
            }

            foreach (var arr in graphArr) {
                Graph[arr[0]].Add(arr[1]);
                Graph[arr[1]].Add(arr[0]);
            }
        }
    }

    /// <summary>有向グラフの隣接行列表現</summary>
    class AdjacencyMatrixCreator {
        public int[][] Matrix { get; }

        public AdjacencyMatrixCreator(int[][] matrix) {
            this.Matrix = matrix;
        }
        /// <summary>ノード数nの隣接行列表現を生成.1オリジン</summary>
        public AdjacencyMatrixCreator(int n, int defaultVal = 0) {
            Matrix = new int[n + 1][];
            for (int i = 0; i <= n; i++) {
                Matrix[i] = new int[n + 1];
                for (int j = 0; j <= n; j++) {
                    Matrix[i][j] = defaultVal;
                }
            }
        }

        /// <summary>
        /// 辺の追加 (行列に1を足す)
        /// </summary>
        /// <param name="nodeNo">元ノードのNo</param>
        /// <param name="outDegree">出次数</param>
        /// <param name="outNodeNos">向かう先のノードNo</param>
        public void AddEdgeVal(int nodeNo, int outDegree, int[] outNodeNos) {
            foreach (var outNodeNo in outNodeNos) {
                Matrix[nodeNo][outNodeNo] = 1;
            }
        }

        /// <summary>
        /// 辺の削除 (行列を0にする)
        /// </summary>
        /// <param name="nodeNo">元ノードのNo</param>
        /// <param name="outDegree">出次数</param>
        /// <param name="outNodeNos">向かう先のノードNo</param>
        public void DelEdgeVal(int nodeNo, int outDegree, int[] outNodeNos) {
            foreach (var outNodeNo in outNodeNos) {
                Matrix[nodeNo][outNodeNo] = 0;
            }
        }
    }
}

namespace Sort {
    static class MergeSort {
        /// <summary>ソートを行う（mutable）</summary>
        public static void Sort(long[] arr) {
            Sort(arr, 0, arr.Count());
        }


        private static void Sort(long[] sArr, int left, int right) {
            if (left + 1 < right) {
                var mid = (left + right) / 2;
                Sort(sArr, left, mid);  // left ~ mid-1をソート
                Sort(sArr, mid, right); // mid ~ rightをソート
                Merge(sArr, left, mid, right);  // 昇順に並んだ2つの列を結合する。
            }
        }

        // arrのleftとrightは昇順に並ぶのでO(n1+n2)
        private static void Merge(long[] arr, int left, int mid, int right) {
            var lArr = new long[mid - left + 1];
            var rArr = new long[right - mid + 1];
            for (int i = 0; i < mid - left; i++) {
                lArr[i] = arr[left + i];
            }
            lArr[mid - left] = long.MaxValue;

            for (int i = 0; i < right - mid; i++) {
                rArr[i] = arr[mid + i];
            }
            rArr[right - mid] = long.MaxValue;

            int l = 0, r = 0;
            // arrを並び替える

            for (int i = left; i < right; i++) {
                if (lArr[l] > rArr[r]) {
                    arr[i] = rArr[r];
                    r++;
                } else {
                    arr[i] = lArr[l];
                    l++;
                }
            }
        }
    }

    static class QuickSort {
        internal static void Sort(int[] ts) {
            Sort(ts, 0, ts.Count() - 1);
        }
        /// <summary>Quickソート</summary>
        private static void Sort(int[] ts, int p, int r) {
            if (p < r) {
                var q = Partition(ts, p, r);
                Sort(ts, p, q - 1);
                Sort(ts, q + 1, r);
            }
        }

        public static int Partition(int[] sArr, int p, int r) {
            var x = sArr[r];
            var i = p - 1;
            for (int j = p; j < r; j++) {
                if (sArr[j] <= x) {
                    i++;
                    sArr.Swap(i, j);
                }
            }
            sArr.Swap(i + 1, r);
            return i + 1;
        }
    }

    /// <summary>計数ソート</summary>
    static class CountingSort {
        public static int[] Sort(int[] aArr, int k) {
            int[] b, c;
            var n = aArr.Length;
            b = new int[n];
            c = Enumerable.Repeat(0, k + 1).ToArray();


            for (int i = 0; i < n; i++) {
                c[aArr[i]]++;
            }

            for (int i = 1; i <= k; i++) {
                c[i] += c[i - 1];
            }

            for (int i = n - 1; i >= 0; i--) {
                b[c[aArr[i]] - 1] = aArr[i];
                c[aArr[i]]--;
            }

            return b;
        }
    }
}

namespace Tree {
    /// <summary>UnionFind木</summary>
    public class UnionFind {
        Dictionary<int, int> nodeToRoot;
        Dictionary<int, int> rank; // その根が配下に何個nodeがぶら下がっているか

        public UnionFind(int n, int startAt = 0) {
            nodeToRoot = Enumerable.Range(startAt, n).ToDictionary(i => i, i => i);    // n個根を作る
            rank = Enumerable.Range(startAt, n).ToDictionary(i => i, i => 0);
        }

        /// <summary>根を得る</summary>
        public int GetRoot(int x) {
            if (nodeToRoot[x] == x) { // 渡されたノードが根
                return x;
            }
            nodeToRoot[x] = GetRoot(nodeToRoot[x]);  // parentの根を得てset。調べるたびに最適化。

            return nodeToRoot[x];
        }

        /// <summary>xとyの木を併合 => 根を同一にする</summary>
        public void Unite(int x, int y) {
            var rootX = GetRoot(x);
            var rootY = GetRoot(y);

            if (rootX == rootY) {
                return; // pass
            }

            if (rank[rootX] < rank[rootY]) {    // yをxへ併合
                nodeToRoot[rootX] = nodeToRoot[rootY];
            } else {
                nodeToRoot[rootX] = nodeToRoot[rootY];

                if (rank[rootX] == rank[rootY]) {
                    rank[rootX]++;  // yの下に配下が1つ追加される。
                }
            }
        }

        public bool AreSameRoot(int x, int y) {
            return GetRoot(x) == GetRoot(y);
        }
    }

    /// <summary>根付き木</summary>
    public class RootedTree {
        readonly Dictionary<int, Node> nodes;
        const int NIL = -1;
        int startAt;
        int nNode;

        class Node {
            /// <summary>親のID</summary>
            public int Parent { get; set; }
            /// <summary>子の最左端</summary>
            public int LeftChild { get; set; }
            /// <summary>右の兄弟</summary>
            public int RightBrother { get; set; }
            /// <summary>深さ</summary>
            public int Depth { get; set; }

            public Node() {
                Parent = NIL;
                LeftChild = NIL;
                RightBrother = NIL;
                Depth = 0;
            }
        }

        /// <summary>根付き木</summary>
        public RootedTree(int n, int startAt = 0) {
            this.nodes = Enumerable.Range(startAt, n).ToDictionary(i => i, i => new Node());
            this.startAt = startAt;
            this.nNode = n;
        }

        /// <summary>Nodeに子を追加</summary>
        public void SetChilds(int id, int[] childs) {
            if (childs.Length == 0) {
                return;
            }

            nodes[id].LeftChild = childs.First();

            for (int i = 0; i < childs.Length - 1; i++) {
                int childId = childs[i];
                nodes[childId].Parent = id;
                nodes[childId].RightBrother = childs[i + 1];
            }

            nodes[childs.Last()].Parent = id;
        }

        /// <summary>親Idを取得</summary>
        public int GetParentId(int id) {
            return nodes[id].Parent;
        }

        /// <summary>子のidを取得</summary>
        public int[] GetChilds(int id) {
            var childs = new List<int>();

            var child = nodes[id].LeftChild;
            while (child != NIL) {
                childs.Add(child);
                child = nodes[child].RightBrother;
            }

            return childs.ToArray();
        }

        int Root = NIL;
        public int GetRoot() {
            if (Root == NIL) {
                var current = nodes.First().Key;  // 何でもいい
                var parent = nodes[current].Parent;

                while (parent != NIL) {
                    current = parent;
                    parent = nodes[current].Parent;
                }

                Root = current;
            }
            return Root;
        }

        bool isSetedDepth = false;
        /// <summary>深さを取得</summary>
        public int GetDepth(int id) {
            if (!isSetedDepth) {
                var root = GetRoot();
                SetDepth(root, depth: 0);
                isSetedDepth = true;
            }

            var current = id;
            var parent = nodes[id].Parent;
            var depth = 0;
            while (parent != NIL) {
                current = parent;
                parent = nodes[current].Parent;
                depth++;
            }

            return depth;
        }

        private void SetDepth(int id, int depth) {
            var node = nodes[id];
            node.Depth = depth;

            if (node.RightBrother != NIL) {
                SetDepth(node.RightBrother, depth);
            }

            if (node.LeftChild != NIL) {
                SetDepth(node.LeftChild, depth + 1);
            }

        }

        public int NodesCount() {
            return nodes.Count();
        }
    }

    /// <summary>二分木</summary>
    public class BinaryTree {
        const int NIL = int.MinValue;
        readonly int startAt;
        readonly int nNode;
        class Node {
            public int Parent { get; set; }
            public int LeftChild { get; set; }
            public int RightChild { get; set; }

            public int Depth { get; set; }
            public int Height { get; set; }
            /// <summary>唯一の兄弟</summary>
            public int Sibling { get; set; }

            public Node() {
                Parent = NIL;
                LeftChild = NIL;
                RightChild = NIL;
                Sibling = NIL;

                Depth = 0;
                Height = 0;
            }
        }


        Dictionary<int, Node> nodes;
        public BinaryTree(int n, int startAt) {
            this.nodes = Enumerable.Range(startAt, n).ToDictionary(i => i, i => new Node());
            this.startAt = startAt;
            this.nNode = n;
        }

        /// <summary>
        /// 子をセットする
        /// </summary>
        /// <param name="id">ノードのID</param>
        /// <param name="left">左子ID。存在しない場合は-1</param>
        /// <param name="right">右子ID。存在しない場合は-1</param>
        public void SetChilds(int id, int left, int right) {
            nodes[id].LeftChild = left;
            nodes[id].RightChild = right;

            if (left != NIL) {
                nodes[left].Parent = id;
                nodes[left].Sibling = right;
            }

            if (right != NIL) {
                nodes[right].Parent = id;
                nodes[right].Sibling = left;
            }
        }

        /// <summary>Root</summary>
        int Root = NIL;
        public int GetRoot() {
            if (Root == NIL) {
                var current = nodes.First().Key;
                var parent = nodes[current].Parent;

                while (parent != NIL) {
                    current = parent;
                    parent = nodes[current].Parent;
                }

                Root = current;
            }

            return Root;
        }

        #region Depth
        bool isSetDepth = false;
        public int GetDepth(int id) {
            if (!isSetDepth) {
                var root = GetRoot();
                SetDepth(root, 0);
                isSetDepth = true;
            }

            return nodes[id].Depth;
        }

        private void SetDepth(int id, int depth) {
            nodes[id].Depth = depth;

            if (nodes[id].LeftChild != NIL) {
                SetDepth(nodes[id].LeftChild, depth + 1);
            }

            if (nodes[id].RightChild != NIL) {
                SetDepth(nodes[id].RightChild, depth + 1);
            }
        }
        #endregion

        #region Height
        bool isSetHeight = false;
        /// <summary>高さを得る</summary>
        public int GetHeight(int id) {
            if (!isSetHeight) {
                var root = GetRoot();
                SetHeight(root);
                isSetHeight = true;
            }

            return nodes[id].Height;
        }

        public int SetHeight(int id) {
            var node = nodes[id];
            int hLeft = 0;
            int hRight = 0;

            if (node.LeftChild != NIL) {
                hLeft = SetHeight(node.LeftChild) + 1;
            }

            if (node.RightChild != NIL) {
                hRight = SetHeight(node.RightChild) + 1;
            }

            node.Height = Math.Max(hLeft, hRight);
            return node.Height;
        }
        #endregion

        /// <summary>兄弟を得る</summary>
        public int GetSibling(int id) {
            return nodes[id].Sibling;
        }

        /// <summary>親IDを得る</summary>
        public int GetParent(int id) {
            return nodes[id].Parent;
        }

        public int[] GetChilds(int id) {
            var ids = new List<int>();
            if (nodes[id].LeftChild != NIL) {
                ids.Add(nodes[id].LeftChild);
            }

            if (nodes[id].RightChild != NIL) {
                ids.Add(nodes[id].RightChild);
            }

            return ids.ToArray();
        }

        /// <summary>左子を取得。存在しない時はint.MinValue</summary>
        public int GetLeft(int id) {
            return nodes[id].LeftChild;
        }

        /// <summary>右子を取得。存在しない時はint.MinValue</summary>
        public int GetRight(int id) {
            return nodes[id].RightChild;
        }

        public int NodesCount() {
            return nodes.Count();
        }

        #region 巡回
        private enum OrderType {
            Pre, In, Post
        }

        /// <summary>先行順巡回のNodeIdを取得</summary>
        public IEnumerable<int> GetPreOrder() {
            return Process(OrderType.Pre);
        }

        /// <summary>中間順巡回のNodeIdを取得</summary>
        public IEnumerable<int> GetInOrder() {
            return Process(OrderType.In);
        }

        /// <summary>後方順巡回のNodeIdを取得</summary>
        public IEnumerable<int> GetPostOrder() {
            return Process(OrderType.Post);
        }

        /// <summary>巡回を出力。O(N)</summary>
        private IEnumerable<int> Process(OrderType orderType) {
            var order = new List<int>();
            var stack = new Stack<int>();
            var passed = Enumerable.Range(startAt, nNode).ToDictionary(i => i, i => false); // 巡回済
            var orderAdded = Enumerable.Range(startAt, nNode).ToDictionary(i => i, i => false); // 処理済

            Action<int> addOrder = id => {
                if (!orderAdded[id]) {
                    order.Add(id);
                    orderAdded[id] = true;
                }
            };

            stack.Push(GetRoot());

            while (stack.Count > 0) {
                var current = stack.First();
                passed[current] = true;

                if (orderType == OrderType.Pre) {
                    addOrder(current); // 先行順巡回
                }

                int left = GetLeft(current);
                if (left != -1 && !passed[left]) {
                    stack.Push(left);
                    continue;
                }

                if (orderType == OrderType.In) {
                    addOrder(current); // 中間順巡回
                }

                int right = GetRight(current);
                if (right != -1 && !passed[right]) {
                    stack.Push(right);
                    continue;
                }

                if (orderType == OrderType.Post) {
                    addOrder(current);　 // 後方順巡回
                }

                stack.Pop();
            }

            return order;
        }
        #endregion
    }

    /// <summary>二分探索木</summary>
    public class BinarySearchTree {
        static readonly Node NIL = new Node(int.MinValue);
        class Node {
            public int Key { get; set; }
            public Node Parent { get; set; }
            public Node Left { get; set; }
            public Node Right { get; set; }

            public Node(int key) {
                this.Key = key;
                this.Parent = NIL;
                this.Left = NIL;
                this.Right = NIL;
            }
        }

        Node root = NIL;

        #region Operate
        public void Insert(int key) {
            var z = new Node(key);

            var y = NIL;    // xの親
            var x = root;   // Treeの根

            while (x != NIL) {
                y = x;
                if (z.Key < x.Key) {
                    x = x.Left;
                } else {
                    x = x.Right;
                }
            }

            z.Parent = y;

            if (y == NIL) {
                root = z;
            } else if (z.Key < y.Key) {
                y.Left = z;
            } else {
                y.Right = z;
            }
        }
        public bool Find(int key) {
            var nearLeaf = GetNearLeaf(key);
            return nearLeaf.Key == key;
        }

        private Node GetNearLeaf(int key) {
            var x = this.root;
            while (x != NIL && x.Key != key) {
                if (x.Key < key) {
                    x = x.Right;
                } else {
                    x = x.Left;
                }
            }

            return x;
        }

        public void Delete(int key) {
            var cand = GetNearLeaf(key);
            if (cand.Key != key) {
                throw new KeyNotFoundException();
            }

            DeleteNode(cand);
        }

        private void DeleteNode(Node target) {
            // 削除対象ノードを選択
            Node delTarget;
            if (target.Left == NIL || target.Right == NIL) {
                delTarget = target;
            } else {
                delTarget = NextInOrder(target);
            }

            // DelTargetの親の新規子を決める
            Node newChild;
            if (delTarget.Left != NIL) {
                newChild = delTarget.Left;
            } else {
                newChild = delTarget.Right;
            }

            // 新規子 => 親をつなぐ
            if (newChild != NIL) {
                newChild.Parent = delTarget.Parent;
            }

            // 親 => 新規子をつなぐ
            var parent = delTarget.Parent;
            if (parent == NIL) {
                root = newChild;
            } else if (parent.Left == delTarget) {
                parent.Left = newChild;
            } else if (parent.Right == delTarget) {
                parent.Right = newChild;
            }

            // 消したノードの値を渡されたNodeに入れる
            if (delTarget != target) {
                target.Key = delTarget.Key;
            }
        }

        /// <summary>次の中間順巡回のNodeを取得</summary>
        /// <returns>NILの時は木が空</returns>
        private Node NextInOrder(Node node) {
            if (node.Right == NIL) {
                var current = node;
                var parent = node.Parent;

                // 親をたどっていき、初めて左子となっているNodeを取得
                while (parent != NIL && current == parent.Right) {
                    current = parent;
                    parent = current.Parent;
                }

                return node.Parent;
            }

            // 右部分木より最左を取得
            var root = node.Right;
            while (root.Left != NIL) {
                root = root.Left;
            }

            return root;
        }

        /// <summary>
        /// targetの親の子を、新しい子につなぎ替える
        /// </summary>
        /// <param name="target">対象のTarget</param>
        /// <param name="newChild">つなぎ変えた先の子</param>
        private static void ChangeParentOfChild(Node target, Node newChild) {
            var parent = target.Parent;
            if (parent.Left == target) {
                parent.Left = newChild;
            } else if (parent.Right == target) {
                parent.Right = newChild;
            } else {
                throw new KeyNotFoundException();
            }
        }

        #endregion

        #region Order
        public IEnumerable<int> GetInOrder() {
            var orderGetter = new OrderGetter();
            return orderGetter.GetInOrder(root);
        }

        public IEnumerable<int> GetPreOrder() {
            var orderGetter = new OrderGetter();
            return orderGetter.GetPreOrder(root);
        }

        class OrderGetter {
            private List<int> process = new List<int>();

            public IEnumerable<int> GetInOrder(Node root) {
                SetInOrder(root);
                return process;
            }

            public IEnumerable<int> GetPreOrder(Node root) {
                SetPreOrder(root);
                return process;
            }

            private void SetInOrder(Node node) {
                if (node == NIL) {
                    return;
                }

                SetInOrder(node.Left);
                process.Add(node.Key);
                SetInOrder(node.Right);
            }

            private void SetPreOrder(Node node) {
                if (node == NIL) {
                    return;
                }
                process.Add(node.Key);
                SetPreOrder(node.Left);
                SetPreOrder(node.Right);
            }
        }
        #endregion
    }

}