#include <iostream>
#include <vector>
#include<map>

using namespace std;

/****************************************************************************/
// メモ - UnionFindとは -                                                    
//                                                                          
//      UnionFindは、グループを管理するときに便利なグラフのひとつ。             
//      グループをは、グループの代表（以降、「親」という）を決め、               
//     「〇〇が属するグループ」と管理する。   
//      この時、グラフ（木）の形になる。                                   
//                                                                          
//      このclassでは0からnのノードを持つUnionFind木を作成する。                
//      内部構造を知らなくても、ノードの数さえ知っていれば一連の操作が可能    
//
//      このclassの操作関数は、
//          UniTree
//              UniTree型の変数を作る関数（コンストラクター） 
//  
//          int FindSet(int x)
//              ノードxの親を求める関数
//          
//          bool Same(int x, int y)
//              ノードxとノードyが同じにグループに
//              所属しているかを返す関数
//
//          void Unite(int x, int y)
//              ノードxとノードyを同じグループに
//              所属させる関数
//
//      以上4つである。必要であれば適宜追加してね。
/****************************************************************************/
class UniTree { 
    public:
    
    // par[i]       : ノードiの親
    // hight[i]     : ノードiの高さ
    vector<int> par, hight;

    //  [引数]   
    //          int size            : UnionFind木の大きさ
    //
    //  [機能]   
    //          UnionFind木の初期化（親と高さの初期化）
    UniTree() {}
    UniTree(int size){
        par = vector<int>(size + 1, 0);
        hight = vector<int>(size + 1, 0);
        for(int i=0 ;i<=size ;i++ ){
            par[i] = i;
        }
    }

    //  [引数]   
    //          int x   : 親を求めたいノードの番号
    //
    //  [機能]   
    //          ノードxの親を求めながら、再帰的に経路圧縮を行う
    int FindSet(int x){
        if(par[x] != x){
            par[x] = FindSet(par[x]);
    //      return(FindSet(par[x]));経路圧縮をしないので遅くなるが変形しない
        } 
        
        return (par[x]);
    }

    //  [引数]   
    //          int x   : ノードx
    //          int y   : ノードy
    //
    //  [機能]   
    //          ノードxとノードyが同じグループならtrue、
    //          そうでなければfalseを返す
    bool Same(int x, int y){
        int X, Y;

        X = FindSet(x);
        Y = FindSet(y);

        if(X == Y){
            return (true);
        } else {
            return (false);
        }
    }

    //  [引数]   
    //          int x   : ノードx
    //          int y   : ノードy
    //
    //  [機能]   
    //          ノードxとノードyが同じグループに属するようにする
    void Link(int x, int y){
        int X, Y;

        X = FindSet(x);
        Y = FindSet(y);

        if(hight[X] > hight[Y]){
            par[Y] = X;
        } 
        else if(hight[Y] > hight[X]){
            par[X] = Y;    
        } else {
            par[X] = Y;
            hight[Y]++;
        }

        return ;
    }

    /****************************************************************************/
    // メモ - UnionFindのメソッド （操作関数群） -                            
    //                                                                     
    // UnionFind(int size)                                                    
    //  UnionFind型の変数を作る関数（コンストラクター）。
    //  作る時は、                          
    //                                                                   
    //  『UniTree 変数名 = UniTree 大きさ;』
    //     で作れる。
    //
    //   詳しい構造はよく知らない。螺旋本を丸パクリした。
    //  どうやらclassでコンストラクター（ユーザー定義型の変数を作るメソッド）は、
    //  class名と同じ名前でないといけないらしい。
    //
    //  初期化について
    //    hight(高さ)をすべて0にしたのは、すべてのノードの下にノードがないから。
    //    par（親）を自分自身にしたのは、すべてのノードが『自分自身が親のグループ』、
    //    つまり自分しかそのグループに所属していないから。 
    // ---------------------------------------------------------------------------
    // 
    // int FindSet(int x)
    //   ノードxの親を求める関数。
    //   これ以降のコンストラクターではない関数を呼び出すときは、
    //
    //   『変数名.FindSet(ノードの番号）』
    //     で呼び出せる。
    //
    //  「ノードxの親はノードy」→「ノードyの親はノードz」→「ノードzの親はノードz」
    //   したがって、「ノードxの親はノードz」となる。
    //   ノードxの親を求めると同時に、ノードxの親の更新を使っている
    //   いつも『自分の親』の親が自分自身とは限らない。
    //
    //   経路圧縮について
    //     経路圧縮は、グループの構造は壊れず、次に親を求める時に高速になるので
    //     積極的にしよう。（グラフの形は死んでしまう）
    // ---------------------------------------------------------------------------
    //
    // bool Same(int x, int y)
    //   ノードxとノードyが同じグループに属しているなら"true"、
    //   そうでなければ"false"を返す。
    //   ノードxの親とノードyの親が同じなら、同じグループに属していると言える
    //   
    //   ちなみに、ノードxの親をX、ノードyの親をYとしてるけど、
    //   C/C++ では変数名に大文字を使わないほうがいい。
    // ---------------------------------------------------------------------------
    //
    // void Link(int x, int y)
    //   ノードxとノードyを同じグループに所属させる。
    //   「『ノードxの親』の親をノードyの親にする」もしくは、
    //   「『ノードyの親』の親をノードxの親にする」
    //    という言い方のほうが正しいかもしれない。
    //   
    //    効率化のため、ノードx,yのうち、そこまでの高さが高いほうが親となる
    //    高さが同じだった時も高さの更新している
    //
    /****************************************************************************/
};

signed main()
{
   int n,m,a[100],b[100],ans=0,top;
   cin>>n>>m;
   UniTree tree=UniTree(n);
   for(int i=0;i<m;i++){
        cin>>a[i]>>b[i];
        tree.Link(a[i]-1,b[i]-1);
   }
    
    top=tree.FindSet(0);
    for(int i=0;i<m;i++){
        UniTree tree1=UniTree(n);
        for(int j=0;j<m;j++){
            if(i!=j){
                tree1.Link(a[j]-1,b[j]-1);
            }
        }
        for(int j=0;j<n;j++){
            for(int k=j+1;k<n;k++){
                if(!tree1.Same(j,k)){
                    ans++;
                    goto c;
                }
            }
        }
        c:;
    }
    cout<<ans<<'\n';
    return (0);
}

Measured memory_bytes: 3632