Problem:
Score : 400 points

Problem Statement

There are N cities. There are also K roads and L railways, extending between the cities.
The i-th road bidirectionally connects the p_i-th and q_i-th cities, and the i-th railway bidirectionally connects the r_i-th and s_i-th cities.
No two roads connect the same pair of cities. Similarly, no two railways connect the same pair of cities.

We will say city A and B are connected by roads if city B is reachable from city A by traversing some number of roads. Here, any city is considered to be connected to itself by roads.
We will also define connectivity by railways similarly.

For each city, find the number of the cities connected to that city by both roads and railways.

Constraints

2 ≦ N ≦ 2*10^5

1 ≦ K, L≦ 10^5

1 ≦ p_i, q_i, r_i, s_i ≦ N

p_i < q_i

r_i < s_i

When i ≠ j, (p_i, q_i) ≠ (p_j, q_j)

When i ≠ j, (r_i, s_i) ≠ (r_j, s_j)

Input

The input is given from Standard Input in the following format:

N K L
p_1 q_1
:
p_K q_K
r_1 s_1
:
r_L s_L

Output

Print N integers. The i-th of them should represent the number of the cities connected to the i-th city by both roads and railways.

Sample Input 1

4 3 1
1 2
2 3
3 4
2 3

Sample Output 1

1 2 2 1

All the four cities are connected to each other by roads.

By railways, only the second and third cities are connected. Thus, the answers for the cities are 1, 2, 2 and 1, respectively.

Sample Input 2

4 2 2
1 2
2 3
1 4
2 3

Sample Output 2

1 2 2 1

Sample Input 3

7 4 4
1 2
2 3
2 5
6 7
3 5
4 5
3 4
6 7

Sample Output 3

1 1 2 1 2 2 2

Solution in C# (Mono 4.6.2.0) (judge verdict: Time Limit Exceeded)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ContestLibrary;

namespace ABC_D
{
    class D049 //解説AC
    {
        public static void Main(string[] args)
        {
            int[] a = Console.ReadLine().Split(' ').Select(x => int.Parse(x)).ToArray();
            int N = a[0], K = a[1], L = a[2];
            Graph g1 = new Graph(N);
            Graph g2 = new Graph(N);

            for (int i = 0; i < K; i++)
            {
                int[] b = Console.ReadLine().Split(' ').Select(x => int.Parse(x)).ToArray();
                int p = b[0], q = b[1];
                g1.AddEdge(p, q);
            }
            for (int i = 0; i < L; i++)
            {
                int[] b = Console.ReadLine().Split(' ').Select(x => int.Parse(x)).ToArray();
                int p = b[0], q = b[1];
                g2.AddEdge(p, q);
            }
            HashSet<int>[] s1 = g1.GetSeparatedVertexSet();
            HashSet<int>[] s2 = g2.GetSeparatedVertexSet();
            int[] i1 = new int[N + 1];
            int[] i2 = new int[N + 1];

            foreach(Vertex vx in g1.Vertices)
            {
                i1[vx.Id] = vx.ConnectGraphNo;
            }
            foreach(Vertex vx in g2.Vertices)
            {
                i2[vx.Id] = vx.ConnectGraphNo;
            }

            int[] r = new int[N];
            Dictionary<Pair, int> dic = new Dictionary<Pair, int>();
            for (int i = 1; i <= N; i++)
            {
                Pair pr = new Pair(); pr.i1 = i1[i]; pr.i2 = i2[i];
                if (!dic.ContainsKey(pr))
                {
                    dic.Add(pr, 0);
                }
                dic[pr]++;
            }

            for(int i = 1; i <= N; i++)
            {
                Pair pr = new Pair(); pr.i1 = i1[i]; pr.i2 = i2[i];
                r[i - 1] = dic[pr];
            }

            Console.WriteLine(string.Join(" ", r));

        }
    }

    struct Pair
    {
        public int i1;
        public int i2;
    }

}

namespace ContestLibrary
{

    class Graph
    {
        public bool UseMatrix { get; set; }
        private bool idZeroStart;
        public List<Vertex> Vertices { get; }
        public List<Edge> Edges { get; }
        public double[,] Matrix { get; private set; }
        public bool IsDirectedGraph { get; }

        public Graph(int N, bool isDirectedGraph = false, bool idZeroStart = false, bool useMatrix = false)
        {
            IsDirectedGraph = isDirectedGraph;
            this.idZeroStart = idZeroStart;
            Vertices = new List<Vertex>();
            Edges = new List<Edge>();

            if (useMatrix) Matrix = new double[N, N];

            for (int i = 0; i < N; i++)
            {
                int id = idZeroStart ? i : i + 1;
                Vertex n = new Vertex(id);
                Vertices.Add(n);
            }
        }

        //隣接行列方式でのセットアップ
        public void SetAdjancencyMatrix(double[,] matrix)
        {
            Matrix = matrix;

            if (matrix.GetLength(0) != Vertices.Count || matrix.GetLength(1) != Vertices.Count)
            {
                throw new Exception("Invalid Matrix Size");
            }

            for (int i = 0; i < Vertices.Count; i++)
            {
                Vertex vertexL = Vertices[i];
                for (int j = 0; j < Vertices.Count; j++)
                {
                    if (i > j && !IsDirectedGraph) continue;
                    if (matrix[i, j] != double.PositiveInfinity)
                    {
                        Vertex vertexR = Vertices[j];
                        Edge edge = new Edge(vertexL, vertexR, matrix[i, j], IsDirectedGraph);
                        Edges.Add(edge);
                    }
                }
            }

        }


        //隣接リスト方式でのセットアップ
        public void AddAdjacencyList(int idVertex, Dictionary<int, double> adjDictionary)
        {
            int indexL = idZeroStart ? idVertex : idVertex - 1;
            Vertex vertexL = Vertices[indexL];

            foreach (var v in adjDictionary)
            {
                int idR = v.Key;
                double weight = v.Value;
                int indexR = idZeroStart ? idR : idR - 1;
                Vertex vertexR = Vertices[indexR];
                Edge edge = new Edge(vertexL, vertexR, weight, IsDirectedGraph);
                Edges.Add(edge);
                if (Matrix != null)
                {
                    Matrix[indexL, indexR] = weight;
                    if (!IsDirectedGraph) Matrix[indexR, indexL] = weight;
                }
            }
        }
        public void AddAdjacencyList(int idVertex, List<int> adjList)
        {
            Dictionary<int, double> dic = new Dictionary<int, double>();
            foreach (int x in adjList)
            {
                dic.Add(x, 1);
            }
            AddAdjacencyList(idVertex, dic);
        }

        //辺単位でのセットアップ
        public void AddEdge(int idL, int idR, int weight = 1)
        {
            int indexL = idZeroStart ? idL : idL - 1;
            int indexR = idZeroStart ? idR : idR - 1;

            Vertex vertexL = Vertices[indexL];
            Vertex vertexR = Vertices[indexR];
            Edge edge = new Edge(vertexL, vertexR, weight, IsDirectedGraph);
            Edges.Add(edge);
            if (Matrix != null)
            {
                Matrix[indexL, indexR] = weight;
                if (!IsDirectedGraph) Matrix[indexR, indexL] = weight;
            }
        }


        //開始点を指定し、幅優先探索で到達できる
        //頂点とステップ数（辺ウェイト考慮せず）のペアをDictionaryで出力
        public Dictionary<int, int> BreadthFirstSearch(int idS)
        {
            Dictionary<int, int> distances = new Dictionary<int, int>();
            distances.Add(idS, 0);
            int index = idZeroStart ? idS : idS - 1;

            List<Vertex> nodes = new List<Vertex>();
            Vertex nodeStart = Vertices[index];
            nodeStart.Visited = true;
            nodes.Add(nodeStart);
            int depth = 1;
            while (nodes.Count > 0)
            {
                List<Vertex> nextNodes = new List<Vertex>();
                foreach (Vertex node in nodes)
                {
                    foreach (Vertex cn in node.ConnectedVertices)
                    {
                        if (!cn.Visited)
                        {
                            nextNodes.Add(cn);
                            cn.Visited = true;
                            distances.Add(cn.Id, depth);
                        }
                    }
                }
                nodes = nextNodes;
                depth++;
            }
            return distances;
        }

        //開始点を指定し、深さ優先探索
        //TimeFound(訪問時刻)とTimeProcessed(処理完了時刻)を出力
        public int DepthFirstSearch(int idS, int connectGraphNo = 1, int time = 0)
        {
            Stack<Vertex> stack = new Stack<Vertex>();
            int index = idZeroStart ? idS : idS - 1;
            List<Vertex> nodes = new List<Vertex>();
            Vertex nodeStart = Vertices[index];
            nodeStart.Visited = true;

            stack.Push(nodeStart);
            nodeStart.TimeVisited = time;
            nodeStart.ConnectGraphNo = connectGraphNo;

            while (stack.Count > 0)
            {
                Vertex v = stack.Peek();

                if (v.IndexNextSearch >= v.ConnectedVertices.Count)
                {
                    time++;
                    v.TimeProcessed = time;
                    stack.Pop();
                }
                else
                {
                    Vertex vn = v.ConnectedVertices[v.IndexNextSearch];
                    if (!vn.Visited)
                    {
                        time++;
                        stack.Push(vn);
                        vn.Visited = true;
                        vn.TimeVisited = time;
                        vn.ConnectGraphNo = connectGraphNo;
                    }
                    v.IndexNextSearch++;
                }
            }
            return time;
        }

        //全頂点を探索して連結頂点の集合を配列で出力
        public HashSet<int>[] GetSeparatedVertexSet()
        {
            int time = 1;
            int connectNo = 1;
            int index = 0;
            while (index < Vertices.Count)
            {
                int id = idZeroStart ? index : index + 1;
                time = DepthFirstSearch(id, connectNo, time);

                while (index < Vertices.Count && Vertices[index].Visited) index++;
                connectNo++;
                time++;
            }

            HashSet<int>[] sets = new HashSet<int>[connectNo];
            //for (int i = 0; i < sets.Length; i++)
            //{
            //    sets[i] = new HashSet<int>();
            //}

            //foreach (Vertex v in Vertices)
            //{
            //    sets[v.ConnectGraphNo].Add(v.Id);
            //}
            return sets;
        }


        //Prim法で最小全域木を作成し重みの総和を計算(PriorityQueueクラスを利用)
        //辺に着目するためO(m log(m))であまり早くないが殆どの場合十分と思われる
        public double MinimumSpanningTree()
        {
            //最初に選ぶ頂点は何でもよい
            Vertex v0 = Vertices[0];

            bool[] includedVertex = new bool[Vertices.Count + 1];
            double sumWeight = 0;

            //最小全域木に含まれるEdge
            List<Edge> mstEdges = new List<Edge>();
            //現在の木に隣接しているEdgeの優先度付きキュー
            PriorityQueue<Edge> pq = new PriorityQueue<Edge>();

            includedVertex[v0.Id] = true;
            foreach (Edge en in v0.ConnectedEdges)
            {
                pq.Enqueue(en, -en.Weight);
            }

            int counter = 1;
            while (counter < Vertices.Count)
            {
                Edge edge = pq.Dequeue();
                Vertex v;
                //既に両方の頂点が木に含まれているEdgeは除外（含むとループができてしまう)
                if (includedVertex[edge.VertexL.Id] && includedVertex[edge.VertexR.Id]) continue;
                else if (includedVertex[edge.VertexL.Id]) v = edge.VertexR;
                else v = edge.VertexL;
                mstEdges.Add(edge);
                sumWeight += edge.Weight;
                includedVertex[v.Id] = true;

                //新しく木に隣接するようになったEdgeをキューに追加
                foreach (Edge e1 in v.ConnectedEdges)
                {
                    if (includedVertex[e1.VertexL.Id] && includedVertex[e1.VertexR.Id]) continue;
                    pq.Enqueue(e1, -e1.Weight);
                }

                counter++;
            }

            return sumWeight;
        }

        public double[] Dijkstra(int idS)
        {
            double[] distances = Enumerable.Repeat(double.PositiveInfinity, Vertices.Count + 1).ToArray();
            int index = idZeroStart ? idS : idS - 1;
            distances[idS] = 0;

            List<Vertex> confirmedVertices = new List<Vertex>();
            Vertex v0 = Vertices[index];

            PriorityQueue<Vertex> pq = new PriorityQueue<Vertex>();
            pq.Enqueue(v0, 0);

            while (pq.Count > 0)
            {
                Vertex v = pq.Dequeue();
                foreach (Edge e in v.ConnectedEdges)
                {
                    Vertex vn = e.VertexL == v ? e.VertexR : e.VertexL;
                    double distanceNew = distances[v.Id] + e.Weight;
                    if (distanceNew < distances[vn.Id])
                    {
                        distances[vn.Id] = distanceNew;
                        //distanceがより良い値に更新される時、
                        //同一のVertexが複数キューに入ることがあるが、
                        //先に入った（悪い方の）数値は必ず無視されるので問題ない
                        pq.Enqueue(vn, -distances[vn.Id]);
                    }
                }
            }


            return distances;
        }

    }

    class Vertex
    {
        public int Id { get; }
        public List<Edge> ConnectedEdges { get; private set; }
        public List<Vertex> ConnectedVertices { get; private set; }
        public bool Visited { get; set; }
        public int TimeVisited { get; set; }
        public int TimeProcessed { get; set; }
        public int IndexNextSearch { get; set; } //次に探索するVertex番号
        public int ConnectGraphNo { get; set; } //連結している頂点は同じ番号


        public Vertex(int id)
        {
            Id = id;
            ConnectedEdges = new List<Edge>();
            ConnectedVertices = new List<Vertex>();
        }


    }

    class Edge
    {
        public Vertex VertexL { get; }
        public Vertex VertexR { get; }
        public double Weight { get; }

        public Edge(Vertex vertexL, Vertex vertexR, double weight, bool isDirected)
        {
            VertexL = vertexL;
            VertexR = vertexR;
            vertexL.ConnectedEdges.Add(this);
            vertexL.ConnectedVertices.Add(vertexR);
            if (!isDirected)
            {
                vertexR.ConnectedEdges.Add(this);
                vertexR.ConnectedVertices.Add(vertexL);
            }
            Weight = weight;
        }
    }

    //優先度付きキュー（実装はヒープ)
    class PriorityQueue<T>
    {
        private List<HeapNode> heap = new List<HeapNode>();

        public int Count
        {
            get
            {
                return heap.Count;
            }
        }

        public void Enqueue(T item, double priority)
        {
            HeapNode node = new HeapNode(item, priority);
            heap.Add(node);

            int index = heap.Count - 1;

            while (index > 0)
            {
                int indexParent = (index - 1) / 2;
                HeapNode parent = heap[indexParent];
                if (priority > parent.Priority)
                {
                    Swap(index, indexParent);
                    index = indexParent;
                }
                else
                {
                    break;
                }
            }
        }

        public T Dequeue()
        {
            if (heap.Count == 0)
            {
                throw new Exception("No item in priorityQueue");
            }

            T item = heap[0].Node;
            heap[0] = heap[heap.Count - 1];
            heap.RemoveAt(heap.Count - 1);
            if (heap.Count > 0)
            {
                HeapNode node = heap[0];
                int index = 0;
                while (true)
                {
                    int indexL = 2 * index + 1;
                    int indexR = 2 * index + 2;
                    if (indexL >= heap.Count) //子なし(Leaf)
                    {
                        break;
                    }
                    else //子が存在
                    {
                        HeapNode childBetter = heap[indexL];
                        int indexBetter = indexL;
                        if (indexR < heap.Count && heap[indexL].Priority < heap[indexR].Priority)
                        {
                            childBetter = heap[indexR];
                            indexBetter = indexR;
                        }

                        if (node.Priority < childBetter.Priority)
                        {
                            Swap(index, indexBetter);
                            //node = childBetter; 参照なのでスワップで自動的に子を参照してる
                            index = indexBetter;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
            return item;
        }

        public T Peek()
        {
            if (heap.Count == 0)
            {
                throw new Exception("No item in priorityQueue");
            }
            return heap[0].Node;
        }

        private void Swap(int i, int j)
        {
            HeapNode hn = heap[i];
            heap[i] = heap[j];
            heap[j] = hn;
        }


        class HeapNode
        {
            public T Node { get; }
            public double Priority { get; }
            public HeapNode(T item, double priority)
            {
                Node = item;
                Priority = priority;
            }
        }

    }


}