private static void CombineUndirectedCyclicGraphs <TKey>(this Node <TKey, UndirectedCyclicGraph <TKey> > node)
            where TKey : struct
        {
            UndirectedCyclicGraph <TKey> firstGraph = node.GraphsThatIncludeThisNode
                                                      .Cast <UndirectedCyclicGraph <TKey> >().First();

            foreach (var graphToCombineFrom in node.GraphsThatIncludeThisNode
                     .Cast <UndirectedCyclicGraph <TKey> >()
                     .Where(n => !n.Equals(firstGraph))
                     .ToList())
            {
                foreach (var graphsNode in graphToCombineFrom.Nodes.Values)
                {
                    firstGraph.AddNode(graphsNode);
                }
                graphToCombineFrom.ClearNodes();
            }
        }
        public long SolveCase(int numOfCities, int numOfRoads, long libCost, long roadCost, IEnumerable <Tuple <int, int> > roads)
        {
            var easyCasesResult = CheckEasyCases(numOfCities, numOfRoads, libCost, roadCost, roads);

            if (easyCasesResult.HasValue)
            {
                return(easyCasesResult.Value);
            }

            HashSet <UndirectedCyclicGraph <int> > graphs = new HashSet <UndirectedCyclicGraph <int> >();

            Node <int, UndirectedCyclicGraph <int> >[] nodes = Enumerable.Range(1, numOfCities).Select(idx => new Node <int, UndirectedCyclicGraph <int> >(idx)).ToArray();

            foreach (var road in roads)
            {
                var firstNode  = nodes[road.Item1 - 1];
                var secondNode = nodes[road.Item2 - 1];

                if (graphs.Count == 0)
                {
                    graphs.Add(UndirectedCyclicGraph <int> .CreateNewFromFirstEdge(firstNode, secondNode));
                }
                else
                {
                    UndirectedCyclicGraph <int> graph = firstNode.GraphsThatIncludeThisNode.FirstOrDefault() as UndirectedCyclicGraph <int> ??
                                                        secondNode.GraphsThatIncludeThisNode.FirstOrDefault() as UndirectedCyclicGraph <int>;

                    if (graph == null)
                    {
                        graphs.Add(UndirectedCyclicGraph <int> .CreateNewFromFirstEdge(firstNode, secondNode));
                    }
                    else
                    {
                        graph.AddEdgeWithNodes(firstNode, secondNode);
                    }
                }
            }

            var nodesWithNoRoads = CombineAllGraphsOf(nodes);

            return(new HashSet <UndirectedCyclicGraph <int> >(nodes.SelectMany(n => n.GraphsThatIncludeThisNode))
                   .Sum(graph => roadCost * (graph.Nodes.Count - 1) + libCost) + nodesWithNoRoads * libCost);
        }