Problem:
Score : 300 points

Problem Statement

You are given an undirected connected graph with N vertices and M edges that does not contain self-loops and double edges.

The i-th edge (1 \leq i \leq M) connects Vertex a_i and Vertex b_i.

An edge whose removal disconnects the graph is called a bridge.

Find the number of the edges that are bridges among the M edges.

Notes

A self-loop is an edge i such that a_i=b_i (1 \leq i \leq M).

Double edges are a pair of edges i,j such that a_i=a_j and b_i=b_j (1 \leq i<j \leq M).

An undirected graph is said to be connected when there exists a path between every pair of vertices.

Constraints

2 \leq N \leq 50

N-1 \leq M \leq min(N(N−1)⁄2,50)

1 \leq a_i<b_i \leq N

The given graph does not contain self-loops and double edges.

The given graph is connected.

Input

Input is given from Standard Input in the following format:

N M
a_1 b_1
a_2 b_2
:
a_M b_M

Output

Print the number of the edges that are bridges among the M edges.

Sample Input 1

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

Sample Output 1

4

The figure below shows the given graph:

The edges shown in red are bridges. There are four of them.

Sample Input 2

3 3
1 2
1 3
2 3

Sample Output 2

0

It is possible that there is no bridge.

Sample Input 3

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

Sample Output 3

5

It is possible that every edge is a bridge.

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Numerics;


#if DEBUG
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endif

namespace competitive_programming
{
    public class Program
    {
        static void Main(string[] args)
        {
#if DEBUG
            var scanner = new IO.StreamScanner(File.Open("input.txt", FileMode.Open));
#else
            var scanner = new IO.StreamScanner(Console.OpenStandardInput());
#endif

            var n = scanner.Integer();
            var m = scanner.Integer();
            var graph = new BICC(n);
            for (int i = 0; i < m; i++)
                graph.AddEdge(scanner.Integer() - 1, scanner.Integer() - 1);
            graph.GetBicc();

            IO.Printer.Out.WriteLine(graph._bridges.Count());
            IO.Printer.Out.Flush();
        }
        
    }

    // 二重辺連結成分分解
    // http://pakapa104.hatenablog.com/entry/2016/07/24/154557
    public class BICC
    {
        int _n;

        /*** 橋の列挙 ***/
        List<List<int>> _graph;
        List<int> _depth;
        List<int> _par;
        Dictionary<Tuple<int, int>, int> _imosEdge;
        enum STAT { UNUSED, USED_DFS, BRIDGE };
        Dictionary<Tuple<int, int>, STAT> _edgeType;
        public List<Tuple<int, int>> _bridges;

        /*** 二重辺連結成分 ***/
        public List<int> Cmp;               // cmp[i] = 頂点 i がどの二重辺連結成分に属するか
        public int Num_cc;                  // 二重辺連結成分の個数
        public List<int> Size_of_vertex;    // 各二重辺連結成分に含まれる頂点の個数
        public List<List<int>> G_cc;        // 各二重辺連結成分を頂点とする無向グラフ

        public BICC(int n)
        {
            _n = n;
            _graph = Enumerable.Range(0, _n).Select(v => new List<int>()).ToList();
            _edgeType = new Dictionary<Tuple<int, int>, STAT>();
        }

        public void AddEdge(int a, int b)
        {
            _graph[a].Add(b);
            _graph[b].Add(a);
            UpdateEdgeType(a, b, STAT.UNUSED);
        }

        public void GetBicc()
        {
            _imosEdge = new Dictionary<Tuple<int, int>, int>();
            _depth = Enumerable.Range(0, _n).Select(v => -1).ToList();
            _par = Enumerable.Range(0, _n).Select(v => -1).ToList();
            Cmp = Enumerable.Range(0, _n).Select(v => -1).ToList();
            Num_cc = 0;
            _bridges = new List<Tuple<int, int>>();
            Size_of_vertex = new List<int>();
            G_cc = new List<List<int>>();

            /***	橋の列挙	***/
            DfsTreeConstruct(0, -1);

            foreach (var p in _edgeType)
            {
                var e = p.Key;
                var type = p.Value;
                if (type == STAT.UNUSED)
                {
                    UpdateImos(e.Item1, e.Item2);
                }
            }

            ImosFinal(0, -1);
            foreach (var key in _edgeType.Keys.ToList())
            {
                var e = key;
                var type = _edgeType[key];
                if (type == STAT.USED_DFS)
                {
                    if (_imosEdge[e] == 0)
                    {
                        _edgeType[e] = STAT.BRIDGE;
                        _bridges.Add(e);
                    }
                }
            }

            /***	二重辺連結成分	***/
            for (int i = 0; i < _n; i++)
            {
                int size_cc = ExtractCC(i, Num_cc);
                if (size_cc > 0)
                {
                    Size_of_vertex.Add(size_cc);
                    Num_cc++;
                }
            }

            // 頂点の重複を除くためまずsetでつくる
            var G_cc_st = Enumerable.Range(0, Num_cc).Select(v => new HashSet<int>()).ToList();

            foreach (var p in _edgeType)
            {
                var e = p.Key;
                var type = p.Value;
                if (type == STAT.BRIDGE)
                {
                    G_cc_st[Cmp[e.Item1]].Add(Cmp[e.Item2]);
                    G_cc_st[Cmp[e.Item2]].Add(Cmp[e.Item1]);
                }
            }

            for (int i = 0; i < Num_cc; i++)
            {
                G_cc.Add(G_cc_st[i].ToList());
            }
        }

        Tuple<int, int> GetEdge(int a, int b)
        {
            if (a > b)
            {
                var tmp = a;
                a = b;
                b = tmp;
            }
            return new Tuple<int, int>(a, b);
        }

        void UpdateEdgeType(int a, int b, STAT type)
        {
            if (a < 0 || b < 0) return;			// dfsで-1が与えられるとき用
            _edgeType[GetEdge(a, b)] = type;
        }

        void DfsTreeConstruct(int v, int pre)
        {
            if (_depth[v] != -1) return;
            _depth[v] = (pre == -1 ? 0 : _depth[pre] + 1);
            _par[v] = pre;
            UpdateEdgeType(pre, v, STAT.USED_DFS);

            foreach (var nxt in _graph[v])
            {
                if (nxt == pre) continue;
                DfsTreeConstruct(nxt, v);
            }
        }

        void UpdateImos(int a, int b)
        {
            if (_depth[a] < _depth[b])
            {
                var tmp = a;
                a = b;
                b = tmp;
            }

            // depth[a] > depth[b]: DFSで使われなかった a->b の辺
            if (_par[a] != -1)
            {
                var edge = GetEdge(a, _par[a]);
                if (!_imosEdge.ContainsKey(edge)) _imosEdge.Add(edge, 0);
                _imosEdge[edge]++;
            }
            if (_par[b] != -1)
            {
                var edge = GetEdge(b, _par[b]);
                if (!_imosEdge.ContainsKey(edge)) _imosEdge.Add(edge, 0);
                _imosEdge[edge]--;
            }
        }

        int ImosFinal(int v, int pre)
        {
            int t = 0;
            foreach (var nxt in _graph[v])
            {
                if (nxt != pre && _edgeType[GetEdge(nxt, v)] == STAT.USED_DFS)
                {
                    t += ImosFinal(nxt, v);
                }
            }
            var edge = GetEdge(v, pre);

            if (pre != -1)
            {
                if (!_imosEdge.ContainsKey(edge)) _imosEdge.Add(edge, 0);
                _imosEdge[edge] += t;
            }
            return pre == -1 ? 0 : _imosEdge[edge];
        }

        // 1つの連結成分を取り出し、その連結成分の大きさを返す
        int ExtractCC(int v, int color)
        {
            if (Cmp[v] != -1) return 0;
            Cmp[v] = color;
            int t = 1;
            foreach (var nxt in _graph[v])
            {
                if (_edgeType[GetEdge(v, nxt)] != STAT.BRIDGE)
                {
                    t += ExtractCC(nxt, color);
                }
            }
            return t;
        }
    }
}

#if DEBUG
#endif

#region INOUT

namespace IO
{
    using System.IO;
    using System.Text;
    using System.Globalization;

    public class Printer : StreamWriter
    {
        static Printer()
        {
            Out = new Printer(Console.OpenStandardOutput()) { AutoFlush = false };
        }

        public static Printer Out { get; set; }

        public override IFormatProvider FormatProvider
        {
            get { return CultureInfo.InvariantCulture; }
        }

        public Printer(Stream stream)
            : base(stream, new UTF8Encoding(false, true))
        {
        }

        public Printer(Stream stream, Encoding encoding)
            : base(stream, encoding)
        {
        }

        public void Write<T>(string format, T[] source)
        {
            base.Write(format, source.OfType<object>().ToArray());
        }

        public void WriteLine<T>(string format, T[] source)
        {
            base.WriteLine(format, source.OfType<object>().ToArray());
        }
    }

    public class StreamScanner
    {
        public StreamScanner(Stream stream)
        {
            str = stream;
        }

        public readonly Stream str;
        private readonly byte[] buf = new byte[1024];
        private int len, ptr;
        public bool isEof;

        public bool IsEndOfStream
        {
            get { return isEof; }
        }

        private byte read()
        {
            if (isEof) return 0;
            if (ptr < len) return buf[ptr++];
            ptr = 0;
            if ((len = str.Read(buf, 0, 1024)) > 0) return buf[ptr++];
            isEof = true;
            return 0;
        }

        public char Char()
        {
            byte b;
            do b = read(); while ((b < 33 || 126 < b) && !isEof);
            return (char)b;
        }

        public string Scan()
        {
            var sb = new StringBuilder();
            for (var b = Char(); b >= 33 && b <= 126; b = (char)read())
                sb.Append(b);
            return sb.ToString();
        }

        public string ScanLine()
        {
            var sb = new StringBuilder();
            for (var b = Char(); b != '\n'; b = (char)read())
                if (b == 0) break;
                else if (b != '\r') sb.Append(b);
            return sb.ToString();
        }

        public long Long()
        {
            if (isEof) return long.MinValue;
            long ret = 0;
            byte b;
            var ng = false;
            do b = read(); while (b != 0 && b != '-' && (b < '0' || '9' < b));
            if (b == 0) return long.MinValue;
            if (b == '-')
            {
                ng = true;
                b = read();
            }
            for (; ; b = read())
            {
                if (b < '0' || '9' < b)
                    return ng ? -ret : ret;
                ret = ret * 10 + b - '0';
            }
        }

        public int Integer()
        {
            return (isEof) ? int.MinValue : (int)Long();
        }

        public double Double()
        {
            var s = Scan();
            return s != "" ? double.Parse(s, CultureInfo.InvariantCulture) : double.NaN;
        }

        static T[] enumerate<T>(int n, Func<T> f)
        {
            var a = new T[n];
            for (int i = 0; i < n; ++i) a[i] = f();
            return a;
        }

        public char[] Char(int n)
        {
            return enumerate(n, Char);
        }

        public string[] Scan(int n)
        {
            return enumerate(n, Scan);
        }

        public double[] Double(int n)
        {
            return enumerate(n, Double);
        }

        public int[] Integer(int n)
        {
            return enumerate(n, Integer);
        }

        public long[] Long(int n)
        {
            return enumerate(n, Long);
        }
    }
}

#endregion