Problem:
Strongly Connected Components

A direced graph is strongly connected if every two nodes are reachable from each other. In a strongly connected component of a directed graph, every two nodes of the component are mutually reachable.

Input

A directed graph G(V, E) and a sequence of queries where each query contains a pair of nodes u and v.

|V| |E|
s0 t0
s1 t1
:
s|E|-1 t|E|-1
Q
u0 v0
u1 v1
:
uQ-1 vQ-1

|V| is the number of nodes and |E| is the number of edges in the graph. The graph nodes are named with the numbers 0, 1,..., |V|-1 respectively.

si and ti represent source and target nodes of i-th edge (directed).

ui and vi represent a pair of nodes given as the i-th query.

Output

For each query, pinrt "1" if the given nodes belong to the same strongly connected component, "0" otherwise.

Constraints

1 ≤ |V| ≤ 10,000

0 ≤ |E| ≤ 30,000

1 ≤ Q ≤ 100,000

Sample Input 1

5 6
0 1
1 0
1 2
2 4
4 3
3 2
4
0 1
0 3
2 3
3 4

Sample Output 1

1
0
1
1

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace Competitive
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            int[] values = Console.ReadLine().Split(' ').Select(int.Parse).ToArray();
            int N = values[0];
            int E = values[1];

            var G = new Graph<int>();
            for (int i = 0; i < N; i++) G.AddNode(i);
            for (int i = 0; i < E; i++)
            {
                int[] _edges = Console.ReadLine().Split(' ').Select(int.Parse).ToArray();
                G.AddEdge(_edges[0], _edges[1]);
            }

            var scc = G.Condensation();
            var A = Util.EmptyList<int>(N);
            for (int i = 0; i < scc.Count; i++)
            {
                for (int j = 0; j < scc[i].Count; j++)
                {
                    int v = scc[i][j];
                    A[v] = i;
                }
            }

            int Q = int.Parse(Console.ReadLine());
            for (int i = 0; i < Q; i++)
            {
                int[] _values = Console.ReadLine().Split(' ').Select(int.Parse).ToArray();
                int q1 = _values[0];
                int q2 = _values[1];
                // Console.Error.WriteLine($"{q1} {q2}");
                Console.WriteLine(A[q1] == A[q2] ? 1 : 0);
            }

        }
    }

        // 有向グラフ
    internal class Graph<T>
    {
        private List<Node> nodes;
        private List<Edge> edges;
        private Dictionary<T, Node> dic;

        private Node this[int index]
        {
            get { return nodes[index]; }
        }

        internal class Node
        {
            public int No;
            public T Obj;
            public List<Edge> Edges;

            public Node(int no, T obj)
            {
                No = no;
                Obj = obj;
                Edges = new List<Edge>();
            }
        }

        internal class Edge
        {
            public int From;
            public int To;
        }
        
        public Graph()
        {
            nodes = new List<Node>();
            edges = new List<Edge>();
            dic = new Dictionary<T, Node>();
        }

        public List<T> Nodes()
        {
            return nodes.Select(n => n.Obj).ToList();
        }

        public List<T> Successors(T obj)
        {
            var node = dic[obj];
            return node.Edges.Select(e => this[e.To].Obj).Distinct().ToList();
        }

        public void AddNode(T obj)
        {
            // TODO: 重複処理
            if (dic.ContainsKey(obj))
            {
                throw new Exception("既にノードに存在します");
            }
            int no = nodes.Count;
            var node = new Node(no, obj);
            dic.Add(obj, node);
            nodes.Add(node);
        }

        public void AddEdge(T fm, T to)
        {
            if (!dic.ContainsKey(fm) || !dic.ContainsKey(to)) throw new Exception("ノードが存在しません");
            var fmIdx = dic[fm].No;
            var toIdx = dic[to].No;
            var edge = new Edge {From = fmIdx, To = toIdx};
            edges.Add(edge);
            this[fmIdx].Edges.Add(edge);
        }

        public List<List<T>> Condensation()
        {
            return CondensationRunner.Run(this);
        }
        
        class CondensationRunner
        {
            private Graph<T> G;
            List<List<int>> scc;
            int n;
            int time;
            List<int> num; // 訪問順
            List<int> low; // 代表点へのリンク - 成分の中で最も訪問順が最初の点
            Stack<int> S; // 成分未確定頂点のスタック
            List<bool> inS; // スタックに入っているかどうかのフラグ

            public static List<List<T>> Run(Graph<T> G)
            {
                var c = new Graph<T>.CondensationRunner();
                c.G = G;
                return c.Condensation();
            }
            
            public List<List<T>> Condensation()
            {
                // 強連結成分分解を行い、それをトポロジカルソート順に出力する
                // http://www.prefield.com/algorithm/graph/strongly_connected_components.html
                // http://qnighy.hatenablog.com/entry/20100102/1262443288
                // http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_3_C&lang=jp で検証可能

                scc = new List<List<int>>();
                n = G.nodes.Count;
                time = 0;
                num = Util.EmptyList<int>(n); // 訪問順
                low = Util.EmptyList<int>(n); // 代表点へのリンク - 成分の中で最も訪問順が最初の点
                S = new Stack<int>(); // 成分未確定頂点のスタック
                inS = Util.EmptyList<bool>(n); // スタックに入っているかどうかのフラグ

                for (int i = 0; i < n; i++)
                {
                    if(num[i] == 0) Visit(i);
                }

                // 深さ優先探索で探索終了順にセットされるため、その逆順にするとトポロジカルソートとなる
                // すべての点が連結でないケースでも問題はない。
                // ->（a->b->c, d->c)ならば、c-b-a-dやc-d-b-aとなるため、その逆順で良い
                // 返り値の型への変換
                var ret = new List<List<T>>();
                foreach (var sc in scc) ret.Add(sc.Select(v=> G[v].Obj).ToList());
                return ret;
            
            }
        
            void Visit(int v)
            {
                time++;
                low[v] = time;
                num[v] = time;
                S.Push(v);
                inS[v] = true;
    
                // 深さ優先探索で連結しているノードを走査する
                foreach (var e in G[v].Edges)
                {
                    int w = e.To;
                    if (num[w] == 0)
                    {
                        Visit(w);
                        // もしノードwがより若い到達順のノードと強連結ならば、ノードvとも強連結
                        low[v] = Math.Min(low[v], low[w]);
                    }
                    else if (inS[w])
                    {
                        // もしノードwがまだ未確定だとしたら、ノードwとは強連結
                        low[v] = Math.Min(low[v], num[w]);
                    }
                }
    
                // 戻ってきたときにlow[v]が更新されていないとすると、vは代表点となる
                // vが代表点であると確定した場合、vより先の未確定の点は同一成分となる
                if (low[v] == num[v])
                {
                    var strongComponent = new List<int>();
                    while (true)
                    {
                        int w = S.Pop();
                        inS[w] = false;
                        strongComponent.Add(w);
                        if (v == w) break;
                    }
                    scc.Add(strongComponent);
                }

        }

        
        }
      
    }
    
    internal static class Util
    {
        public static List<T> EmptyList<T>(int count)
        {
            return Enumerable.Range(0, count).Select(i => default(T)).ToList();
        }
    }

}