#include <bits/stdc++.h>
#include <immintrin.h>

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

#pragma GCC optimize("O3")
#pragma GCC target("avx,avx2,bmi2,fma")

#ifndef lisie_bimbi
#define endl '\n'
#endif

using namespace __gnu_pbds;

#define ordered_set tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update>

using namespace std;


const int mod = 1'000'000'007;

int add(int a, int b){
    if(a + b < mod){
        return a + b;
    }else{
        return a + b - mod;
    }
}


int sub(int a, int b){
    if(a >= b){
        return a - b;
    }else{
        return a - b + mod;
    }
}

using ll = long long;

int mul(int a, int b){
    return ((ll)a * b) % mod;
}

int binpow(int a, int b){
    if(b == 0){
        return 1;
    }
    int ans = binpow(a, b / 2);
    ans = mul(ans, ans);
    if(b % 2 == 1){
        ans = mul(ans, a);
    }
    return ans;
}

int del(int a, int b){
    return mul(a, binpow(b, mod - 2));
}

const int N = 100'000;

int fac[N], fac1[N];

int cnk(int n, int k){
    if(k > n){
        return 0;
    }
    return (mul(fac[n], mul(fac1[k], fac1[n - k])));
}


vector<int> used;

void dfs1(int u, vector<vector<int>> &v, vector<int> &o){
    used[u] = 1;
    for(auto i : v[u]){
        if(!used[i]){
            dfs1(i, v, o);
        }
    }
    o.push_back(u);
}

int tt = 0;

void dfs2(int u, vector<vector<int>> &v1, vector<int> &c){
    used[u] = 1;
    c[u] = tt;
    for(auto i : v1[u]){
        if(!used[i]){
            dfs2(i, v1, c);
        }
    }
}


void solve(){
    fac[0] = 1;
    for(int i = 1; i < N; i++){
        fac[i] = mul(fac[i - 1], i);
    }
    fac1[N - 1] = del(1, fac[N - 1]);
    for(int i = N - 2; i >= 0; i--){
        fac1[i] = mul(fac1[i + 1], i + 1);
    }
    int n, m, g;
    cin >> n >> m >> g;
    vector<pair<int, int>> r;
    for(int i = 0; i < m; i++){
        int a, b;
        cin >> a >> b;
        a--;b--;
        r.emplace_back(a, b);
    }
    vector<vector<int>> c(1 << m, vector<int>(n));
    for(int mask = 0; mask < (1 << m); mask++){
        vector<vector<int>> v(n), v1(n);
        for(int i = 0; i < m; i++){
            if(!((mask >> i) & 1)){
                v[r[i].first].push_back(r[i].second);
                v1[r[i].second].push_back(r[i].first);
            }else{
                v[r[i].second].push_back(r[i].first);
                v1[r[i].first].push_back(r[i].second);
            }
        }
        used.clear();
        used.resize(n);
        vector<int> o;
        for(int i = 0; i < n; i++){
            if(!used[i]){
                dfs1(i, v, o);
            }
        }
        reverse(o.begin(), o.end());
        used.clear();
        used.resize(n);
        tt = 0;
        for(auto i : o){
            if(!used[i]){
                dfs2(i, v1, c[mask]);
                tt++;
            }
        }
        int now = 0;
        vector<int> so(n, -1);
        for(auto &i : c[mask]){
            if(so[i] == -1){
                so[i] = now++;
            }
            i = so[i];
        }
    }
    vector<int> z(1 << m);
    for(int mask = 1; mask < (1 << m); mask++){
        if(c[0] == c[mask]){
            z[mask]++;
        }
    }
    for(int i = 0; i < m; i++){
        for(int mask = 0; mask < (1 << m); mask++){
            if((mask >> i) & 1){
                z[mask] += z[mask ^ (1 << i)];
            }
        }
    }
    int ans = 0, ans2 = 0;
    for(int i = 0; i < (1 << m); i++){
        if(z[i] == 0){
            int x = __builtin_popcount(i);
            if(x > ans){
                ans = x;
                ans2 = 1;
            }else if(x == ans){
                ans2++;
            }
        }
    }
    cout << ans << endl << ans2 << endl;;
}

signed main(){
#ifdef lisie_bimbi
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
#else
    cin.tie(nullptr)->sync_with_stdio(false);
#endif            
    int t = 1;
    //cin >> t;
    while(t--){
        solve();
    }
}