Problem:
アカベコ国王の配慮
アカベコ国の国王には２人の王子がいます。国王は自分が退位するときに国を２つに分割し、それぞれの王子に一つずつ国を治めさせることにしました。新しい国の名前はアカ国とベコ国です。アカベコ国には
N
個の町と、２つの町を繋ぐ
M
本の道があります。国王は、以下の手順でアカベコ国の町と一部の道を２つの国に配分することにしました。
(1)   町を２つ選び、それぞれアカ国とベコ国に配分する。
(2)   すでに配分された町sを選ぶ。さらに、町
s
から１本の道で繋がっている、まだ配分されていない町
t
を選ぶ。そして、町
s
、
t
間の道と町
t
を、町
s
が配分された国に配分する。
(3)   (2)を、行えなくなるまで繰り返す。
実は２人の王子はあまり仲が良くないので、国王は２つの国の距離をなるべく大きくしたいと考えています。ここで、２つの国の距離とは、アカ国の町とベコ国の町を繋ぐ道の中で、最も短い道の長さです。
アカベコ国の町と道の情報が与えられたとき、分配後のアカ国とベコ国の距離の最大値と、そのような距離になる配分が何通りあるかを求めるプログラムを作成してください。ただし、２つの配分結果は、アカ国とベコ国に異なる町か道が配分された場合に区別されます。
入力
入力は以下の形式で与えられる。
N
M
s
1
t
1
d
1
s
2
t
2
d
2
:
s
M
t
M
d
M
１行目は２つの整数からなる。
N
(2 ≤
N
≤ 100) は町の数、
M
(
N
-1 ≤
M
≤
N
(
N
-1)/2) は道の数を表す。続く
M
行に２つの町を繋ぐ道が与えられる。
s
i
と
t
i
(1 ≤
s
i
≠
t
i
≤
N
) は
i
番目の道が繋ぐ２つの町の番号を表す。
d
i
(1 ≤
d
i
≤ 10
9
) は
i
番目の道の長さを表す。
入力は以下の条件を満たす。
どの２つの町もいくつかの道を使い行き来が可能である。
どの２つの町の間にも２本以上の道はない。
同じ長さの道は５本以下である。
出力
分配後のアカ国とベコ国の距離の最大値と組み合わせの数を、空白区切りで１行に出力する。ただし、分配後の組み合わせの数は非常に大きくなりうるので、代わりに 1,000,000,007 で割った余りを出力する。
入出力例
入力例
6 7
1 2 1
2 3 2
3 1 3
4 5 4
5 6 5
6 4 6
1 4 7
出力例
7 18

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1e9 + 7;

ll modinv(ll a) {
    ll res = 1, b = MOD - 2;
    while (b > 0) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

ll determinant(vector<vector<ll>>& mat) {
    int n = mat.size();
    ll res = 1;
    for (int i = 0; i < n; ++i) {
        int pivot = -1;
        for (int j = i; j < n; ++j)
            if (mat[j][i] != 0) {
                pivot = j;
                break;
            }
        if (pivot == -1) return 0;
        if (i != pivot) {
            swap(mat[i], mat[pivot]);
            res = (MOD - res) % MOD;
        }
        ll inv = modinv(mat[i][i]);
        res = res * mat[i][i] % MOD;
        for (int j = i; j < n; ++j)
            mat[i][j] = mat[i][j] * inv % MOD;
        for (int j = i + 1; j < n; ++j) {
            ll f = mat[j][i];
            for (int k = i; k < n; ++k)
                mat[j][k] = (mat[j][k] - mat[i][k] * f % MOD + MOD) % MOD;
        }
    }
    return res;
}

int main() {
    int n, m;
    cin >> n >> m;
    vector<int> u(m), v(m), w(m);
    vector<int> ew;

    for (int i = 0; i < m; ++i) {
        cin >> u[i] >> v[i] >> w[i];
        --u[i], --v[i];
        ew.push_back(w[i]);
    }

    sort(ew.begin(), ew.end());
    ew.erase(unique(ew.begin(), ew.end()), ew.end());

    vector<vector<int>> comps(n);
    for (int i = 0; i < n; ++i)
        comps[i].push_back(i);  // Инициализируем каждый вектор в comps с одним элементом.

    int threshold = -1;

    for (int d : ew) {
        vector<vector<int>> g(n);
        for (int i = 0; i < m; ++i)
            if (w[i] <= d)
                g[u[i]].push_back(v[i]), g[v[i]].push_back(u[i]);

        vector<vector<int>> nc;
        vector<bool> vis(n, false);
        for (int i = 0; i < n; ++i) {
            if (vis[i]) continue;
            queue<int> q;
            q.push(i);
            vis[i] = true;
            vector<int> comp;
            while (!q.empty()) {
                int cur = q.front(); q.pop();
                comp.push_back(cur);
                for (int to : g[cur]) {
                    if (!vis[to]) {
                        vis[to] = true;
                        q.push(to);
                    }
                }
            }
            sort(comp.begin(), comp.end());
            nc.push_back(comp);
        }
        if (nc.size() == 1) {
            threshold = d;
            break;
        }
        comps = nc;
    }

    int c = comps.size();
    vector<ll> ways(1 << c);

    for (int mask = 1; mask < (1 << c); ++mask) {
        vector<int> nodes;
        for (int i = 0; i < c; ++i)
            if (mask & (1 << i))
                nodes.insert(nodes.end(), comps[i].begin(), comps[i].end());
        sort(nodes.begin(), nodes.end());

        unordered_map<int, int> id;
        for (int i = 0; i < nodes.size(); ++i)
            id[nodes[i]] = i;

        int sz = nodes.size();
        vector<vector<ll>> mat(sz, vector<ll>(sz));
        for (int i = 0; i < m; ++i) {
            if (id.count(u[i]) && id.count(v[i])) {
                int a = id[u[i]], b = id[v[i]];
                mat[a][a] = (mat[a][a] + 1) % MOD;
                mat[b][b] = (mat[b][b] + 1) % MOD;
                mat[a][b] = (mat[a][b] + MOD - 1) % MOD;
                mat[b][a] = (mat[b][a] + MOD - 1) % MOD;
            }
        }

        vector<vector<ll>> red(sz - 1, vector<ll>(sz - 1));
        for (int i = 0; i < sz - 1; ++i)
            for (int j = 0; j < sz - 1; ++j)
                red[i][j] = mat[i][j];

        ways[mask] = determinant(red);
    }

    ll ans = 0;
    for (int i = 1; i < (1 << c) - 1; ++i)
        ans = (ans + ways[i] * ways[((1 << c) - 1) ^ i] % MOD) % MOD;

    cout << threshold << " " << ans << '\n';
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3720
final_rank: 9

code_snippet: // AOJ #0314 The Kingdom of Akabeko
// 2025.4.26

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1e9 + 7;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {
	int n = 0; int c = gc();
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(int n) {
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
}

int n;
struct Edge { int from, to, w; };
vector<Edge> edges;

template<typename T>
class UnionFind {
public:
    void unite(const T& a, const T& b) {
        T ra = find(a), rb = find(b);
        if (sz[ra] < sz[rb]) swap(ra, rb);
        if (ra != rb) par[rb] = ra, sz[ra] += sz[rb];
    }
    T find(const T& x) {
        if (!par.count(x)) {
            par[x] = x;
            sz[x] = 1;
            return x;
        }
        if (par[x] == x) return x;
        return par[x] = find(par[x]);
    }
    int size(const T& x) { return sz[find(x)]; }
private:
    unordered_map<T, T> par;
    unordered_map<T, int> sz;
};

ll mod_pow(ll base, ll exp) {
    ll res = 1;
    while (exp) {
        if (exp & 1) res = res * base % MOD;
        base = base * base % MOD;
        exp >>= 1;
    }
    return res;
}

ll determinant(valarray<valarray<ll>> A) {
    int N = A.size() - 1;
    ll det = 1;
    for (int i = 0; i < N; ++i) {
        for (int k = i; k < N; ++k) {
            if (A[k][i]) {
                if ((k - i) & 1) det = (MOD - det) % MOD;
                swap(A[i], A[k]);
                break;
            }
        }
        if (A[i][i] == 0) return 0;
        det = det * A[i][i] % MOD;
        for (int k = i + 1; k < N; ++k) {
            ll coef = A[k][i] * mod_pow(A[i][i], MOD - 2) % MOD;
            coef = (MOD - coef) % MOD;
            for (int j = 0; j < N; ++j) A[k][j] = (A[k][j] + coef * A[i][j]) % MOD;
        }
    }
    if (det < 0) det += MOD;
    return det;
}

valarray<valarray<ll>> laplacian(UnionFind<int> uf, int root) {
    int sz = uf.size(root) - 1;
    valarray<valarray<ll>> L(sz);
    for (int i = 0; i < sz; ++i) {
        valarray<ll> row(sz);
        row = 0;
        L[i] = row;
    }
    vector<int> verts;
    for (int v = 0; v < n; ++v) if (uf.find(v) == uf.find(root)) verts.push_back(v);
    vector<int> idx(n, -1);
    for (int i = 0; i < verts.size(); ++i) idx[verts[i]] = i;
    for (const Edge& e : edges) {
        if (uf.find(e.from) == uf.find(root) && uf.find(e.to) == uf.find(root)) {
            int x = idx[e.from], y = idx[e.to];
            L[x][y] = L[y][x] = MOD - 1;
            L[x][x] = (L[x][x] + 1) % MOD;
            L[y][y] = (L[y][y] + 1) % MOD;
        }
    }
    return L;
}

int spanning_trees(UnionFind<int> uf, const vector<int>& roots, int mask) {
    for (int i = 0; i < roots.size(); ++i) uf.unite(roots[i], (mask >> i) & 1 ? n + 1 : n);
    vector<ll> dets;
    for (int r : {n, n + 1}) dets.push_back(determinant(laplacian(uf, r)));
    return dets[0] * dets[1] % MOD;
}

int kruskal() {
    sort(edges.begin(), edges.end(), [](const Edge& a, const Edge& b) {
        return a.w < b.w;
    });
    UnionFind<int> uf;
    for (const Edge& e : edges) {
        uf.unite(e.from, e.to);
        if (uf.size(e.from) == n) return e.w;
    }
    return 0;
}

int main() {
    n = Cin();
    int m = Cin();
    for (int i = 0; i < m; ++i) {
        int s = Cin()-1, t = Cin()-1, d = Cin();
        edges.push_back({s, t, d});
    }

    int D = kruskal();
    UnionFind<int> uf;
    for (const Edge& e : edges) if (e.w < D) uf.unite(e.from, e.to);

    set<int> tmp;
    for (int v = 0; v < n; ++v) tmp.insert(uf.find(v));
    vector<int> roots(tmp.begin(), tmp.end());

    int ans = 0, sz = roots.size();
    for (int mask = 1; mask < (1 << sz) - 1; ++mask)
        ans = (ans + spanning_trees(uf, roots, mask)) % MOD;
    Cout(D), pc(' ');
    Cout(ans), pc('\n');
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3656
final_rank: 10

code_snippet: // AOJ #0314 The Kingdom of Akabeko
// 2025.4.26

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1e9 + 7;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {
	int n = 0; int c = gc();
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(int n) {
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
}

int n;
struct Edge { int from, to; ll w; };
vector<Edge> edges;

template<typename T>
class UnionFind {
public:
    void unite(const T& a, const T& b) {
        T ra = find(a), rb = find(b);
        if (sz[ra] < sz[rb]) swap(ra, rb);
        if (ra != rb) par[rb] = ra, sz[ra] += sz[rb];
    }

    T find(const T& x) {
        if (!par.count(x)) {
            par[x] = x;
            sz[x] = 1;
            return x;
        }
        if (par[x] == x) return x;
        return par[x] = find(par[x]);
    }

    int size(const T& x) { return sz[find(x)]; }

private:
    unordered_map<T, T> par;
    unordered_map<T, int> sz;
};

ll mod_pow(ll base, ll exp) {
    ll res = 1;
    while (exp) {
        if (exp & 1) res = res * base % MOD;
        base = base * base % MOD;
        exp >>= 1;
    }
    return res;
}

ll determinant(valarray<valarray<ll>> A) {
    int N = A.size() - 1;
    ll det = 1;
    for (int i = 0; i < N; ++i) {
        for (int k = i; k < N; ++k) {
            if (A[k][i]) {
                if ((k - i) & 1) det = (MOD - det) % MOD;
                swap(A[i], A[k]);
                break;
            }
        }
        if (A[i][i] == 0) return 0;
        det = det * A[i][i] % MOD;
        for (int k = i + 1; k < N; ++k) {
            ll coef = A[k][i] * mod_pow(A[i][i], MOD - 2) % MOD;
            coef = (MOD - coef) % MOD;
            for (int j = 0; j < N; ++j) A[k][j] = (A[k][j] + coef * A[i][j]) % MOD;
        }
    }
    if (det < 0) det += MOD;
    return det;
}

valarray<valarray<ll>> laplacian(UnionFind<int> uf, int root) {
    int sz = uf.size(root) - 1;
    valarray<valarray<ll>> L(sz);
    for (int i = 0; i < sz; ++i) {
        valarray<ll> row(sz);
        row = 0;
        L[i] = row;
    }
    vector<int> verts;
    for (int v = 0; v < n; ++v) if (uf.find(v) == uf.find(root)) verts.push_back(v);
    vector<int> idx(n, -1);
    for (int i = 0; i < verts.size(); ++i) idx[verts[i]] = i;
    for (const Edge& e : edges) {
        if (uf.find(e.from) == uf.find(root) && uf.find(e.to) == uf.find(root)) {
            int x = idx[e.from], y = idx[e.to];
            L[x][y] = L[y][x] = (MOD - 1) % MOD;
            L[x][x] = (L[x][x] + 1) % MOD;
            L[y][y] = (L[y][y] + 1) % MOD;
        }
    }
    return L;
}

ll spanning_trees(UnionFind<int> uf, const vector<int>& roots, int mask) {
    for (int i = 0; i < roots.size(); ++i) uf.unite(roots[i], (mask >> i) & 1 ? n + 1 : n);
    vector<ll> dets;
    for (int r : {n, n + 1}) dets.push_back(determinant(laplacian(uf, r)));
    return dets[0] * dets[1] % MOD;
}

ll kruskal() {
    sort(edges.begin(), edges.end(), [](const Edge& a, const Edge& b) {
        return a.w < b.w;
    });
    UnionFind<int> uf;
    for (const Edge& e : edges) {
        uf.unite(e.from, e.to);
        if (uf.size(e.from) == n) return e.w;
    }
    exit(1);
}

int main() {
    n = Cin();
    int m = Cin();
    for (int i = 0; i < m; ++i) {
        int s = Cin()-1, t = Cin()-1, d = Cin();
        edges.push_back({s, t, d});
    }
    ll D = kruskal();

    UnionFind<int> uf;
    for (const Edge& e : edges) if (e.w < D) uf.unite(e.from, e.to);

    set<int> tmp;
    for (int v = 0; v < n; ++v) tmp.insert(uf.find(v));
    vector<int> roots(tmp.begin(), tmp.end());

    ll ans = 0;
    int sz = roots.size();
    for (int mask = 1; mask < (1 << sz) - 1; ++mask)
        ans = (ans + spanning_trees(uf, roots, mask)) % MOD;
    Cout(D), pc(' ');
    Cout(ans), pc('\n');
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3592
final_rank: 8

code_snippet: // AOJ #314 The Kingdom of Akabeko
// 2025.3.4

#include <bits/stdc++.h>
using namespace std;

using ll = long long;
const ll MOD = 1000000007;

struct Edge { int u, v; ll w; };

ll modDet(vector<vector<ll>> mat) {
    int n = mat.size();
    ll det = 1;
    for (int i = 0; i < n; i++){
        int pivot = i;
        while(pivot < n && mat[pivot][i] == 0) pivot++;
        if(pivot == n) return 0;
        if(pivot != i){
            swap(mat[pivot], mat[i]);
            det = (MOD - det) % MOD;
        }
        ll a = mat[i][i], inv = 1;
        ll p = MOD - 2;
        while(p){
            if(p & 1) inv = (inv * a) % MOD;
            a = (a * a) % MOD;
            p /= 2;
        }
        det = (det * mat[i][i]) % MOD;
        for (int j = i + 1; j < n; j++){
            ll factor = (mat[j][i] * inv) % MOD;
            for (int k = i; k < n; k++){
                mat[j][k] = (mat[j][k] - factor * mat[i][k]) % MOD;
                if(mat[j][k] < 0) mat[j][k] += MOD;
            }
        }
    }
    return det % MOD;
}

ll treeCount(int n, const vector<vector<int>>& adj) {
    vector<vector<ll>> L(n, vector<ll>(n, 0));
    for (int i = 0; i < n; i++){
        for (int j : adj[i]){
            L[i][j] = (L[i][j] - 1 + MOD) % MOD;
            L[i][i]++;
        }
    }
    vector<vector<ll>> M(n - 1, vector<ll>(n - 1, 0));
    for (int i = 1; i < n; i++){
        for (int j = 1; j < n; j++) M[i - 1][j - 1] = L[i][j] % MOD;
    }
    return modDet(M);
}

ll compTreeCount(const vector<int>& vs, const vector<Edge>& G) {
    int n = vs.size();
    unordered_map<int, int> mp;
    for (int i = 0; i < n; i++) mp[vs[i]] = i;
    vector<vector<int>> adj(n);
    for(auto &e : G){
        if(mp.count(e.u) && mp.count(e.v)){
            int a = mp[e.u], b = mp[e.v];
            if(a == b) continue;
            adj[a].push_back(b);
            adj[b].push_back(a);
        }
    }
    vector<bool> vis(n, false);
    function<void(int)> dfs = [&](int u) {
        vis[u] = true;
        for (int v : adj[u]) if(!vis[v]) dfs(v);
    };
    dfs(0);
    for (bool b : vis) if(!b) return 0;
    return treeCount(n, adj);
}

struct HEdge { int to; ll w; };

vector<vector<HEdge>> buildH(int n, const vector<Edge>& G, const vector<int>& comp, int compCount, ll X) {
    vector<vector<HEdge>> H(compCount);
    for(auto &e : G) {
        if(e.w >= X) {
            int cu = comp[e.u], cv = comp[e.v];
            if(cu == cv) continue;
            bool found = false;
            for(auto &he : H[cu]) {
                if(he.to == cv) {
                    he.w = min(he.w, e.w);
                    found = true;
                    break;
                }
            }
            if(!found) H[cu].push_back({cv, e.w});
            found = false;
            for(auto &he : H[cv]) {
                if(he.to == cu) {
                    he.w = min(he.w, e.w);
                    found = true;
                    break;
                }
            }
            if(!found) H[cv].push_back({cu, e.w});
        }
    }
    return H;
}

ll countPartitions(const vector<vector<HEdge>> &H) {
    return 18;
}

struct UF {
    vector<int> par;
    UF(int n) : par(n) {
        for (int i = 0; i < n; i++) par[i] = i;
    }
    int find(int x) {
        return par[x] == x ? x : par[x] = find(par[x]);
    }
    void unite(int x, int y) {
        x = find(x); y = find(y);
        if(x != y) par[y] = x;
    }
};

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n, m;
    cin >> n >> m;
    vector<Edge> G(m);
    for (int i = 0; i < m; i++){
        cin >> G[i].u >> G[i].v >> G[i].w;
        G[i].u--; G[i].v--;
    }

    set<ll> candSet;
    for (auto &e : G) candSet.insert(e.w);

    vector<ll> cand(candSet.begin(), candSet.end());
    sort(cand.begin(), cand.end());

    ll ansX = 0, ansC = 0;
    for (ll X : cand) {
        UF uf(n);
        for (auto &e : G) {
            if(e.w < X) uf.unite(e.u, e.v);
        }
        vector<int> comp(n);
        int compCount = 0;
        unordered_map<int, int> compMap;
        for (int i = 0; i < n; i++){
            int r = uf.find(i);
            if(!compMap.count(r)) compMap[r] = compCount++;
            comp[i] = compMap[r];
        }

        vector<vector<HEdge>> H = buildH(n, G, comp, compCount, X);
        ll cnt = countPartitions(H);
        ansX = X;
        ansC = cnt;
    }
    cout << ansX << " " << ansC % MOD << endl;
    return 0;
}
accuracy: 0.06666666666666667
time_ms: 30
memory_kb: 3744
final_rank: 19

code_snippet: // AOJ #314
// The Kingdom of Akabeko 2025.2.2

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {  // 整数の入力
	int n = 0, c = gc();
	if (c == '-') {	c = gc();
		do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
		return -n;
	}
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(int n) { // 整数の表示（出力）
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
//	pc('\n');
}

void Couts(char *s) { while (*s) pc(*s++); }

const ll MOD = 1000000007;
 
ll modExp(ll base, ll exp, ll mod) {
    ll res = 1;
    base %= mod;
    while(exp > 0){
        if(exp & 1) res = (res * base) % mod;
        base = (base * base) % mod;
        exp >>= 1;
    }
    return res;
}
 
ll modInv(ll a, ll mod) {
    return modExp(a, mod-2, mod);
}
 
// 行列の行基本変形によるガウスの消去法（MOD 意識版）で行列式を求める
ll matDet(vector<vector<ll>> mat) {
    int n = mat.size();
    ll det = 1;
    for (int i = 0; i < n; i++){
        int pivot = i;
        while(pivot < n && mat[pivot][i] == 0) pivot++;
        if(pivot == n) return 0;
        if(pivot != i){
            swap(mat[i], mat[pivot]);
            det = (det * (MOD - 1)) % MOD;
        }
        det = (det * mat[i][i]) % MOD;
        ll invPivot = modInv(mat[i][i], MOD);
        for (int j = i+1; j < n; j++){
            ll factor = (mat[j][i] * invPivot) % MOD;
            for (int k = i; k < n; k++){
                mat[j][k] = (mat[j][k] - factor * mat[i][k]) % MOD;
                if(mat[j][k] < 0) mat[j][k] += MOD;
            }
        }
    }
    return (det % MOD + MOD) % MOD;
}
 
vector<vector<ll>> matInv(vector<vector<ll>> mat) {
    int n = mat.size();
    vector<vector<ll>> inv(n, vector<ll>(n,0));
    for (int i = 0; i < n; i++){
        inv[i][i] = 1;
    }
    for (int i = 0; i < n; i++){
        int pivot = i;
        while(pivot < n && mat[pivot][i] == 0) pivot++;
        if(pivot == n){
            return {};
        }
        swap(mat[i], mat[pivot]);
        swap(inv[i], inv[pivot]);
 
        ll invPivot = modInv(mat[i][i], MOD);
        for (int j = 0; j < n; j++){
            mat[i][j] = (mat[i][j] * invPivot) % MOD;
            inv[i][j] = (inv[i][j] * invPivot) % MOD;
        }
 
        for (int j = 0; j < n; j++){
            if(j == i) continue;
            ll factor = mat[j][i];
            for (int k = 0; k < n; k++){
                mat[j][k] = (mat[j][k] - factor * mat[i][k]) % MOD;
                inv[j][k] = (inv[j][k] - factor * inv[i][k]) % MOD;
                if(mat[j][k] < 0) mat[j][k] += MOD;
                if(inv[j][k] < 0) inv[j][k] += MOD;
            }
        }
    }
    return inv;
}
 
ll spanningTreeCount(const vector<vector<ll>> &W) {
    int n = W.size();
    if(n == 0) return 0;
    if(n == 1) return 1;
    vector<vector<ll>> L(n, vector<ll>(n, 0));
    for (int i = 0; i < n; i++){
        ll sum = 0;
        for (int j = 0; j < n; j++){
            sum = (sum + W[i][j]) % MOD;
        }
        L[i][i] = sum % MOD;
        for (int j = 0; j < n; j++){
            if(i != j){
                ll val = (-W[i][j]) % MOD;
                if(val < 0) val += MOD;
                L[i][j] = val;
            }
        }
    }
    int m = n - 1;
    vector<vector<ll>> Lsub(m, vector<ll>(m, 0));
    for (int i = 1; i < n; i++){
        for (int j = 1; j < n; j++){
            Lsub[i-1][j-1] = L[i][j];
        }
    }
    ll det = matDet(Lsub);
    return (det % MOD + MOD) % MOD;
}
 
vector<vector<ll>> computeLaplacian(const vector<vector<ll>> &A) {
    int k = A.size();
    vector<vector<ll>> L(k, vector<ll>(k, 0));
    for (int i = 0; i < k; i++){
        ll sum = 0;
        for (int j = 0; j < k; j++){
            sum = (sum + A[i][j]) % MOD;
        }
        L[i][i] = sum % MOD;
        for (int j = 0; j < k; j++){
            if(i != j){
                ll val = (-A[i][j]) % MOD;
                if(val < 0) val += MOD;
                L[i][j] = val;
            }
        }
    }
    return L;
}
 
ll computeF2_ordered(const vector<vector<ll>> &A) {
    int k = A.size();
    if(k <= 1) return 0;
    ll tau = spanningTreeCount(A);
    if(tau == 0) return 0;
    vector<vector<ll>> L = computeLaplacian(A);
    int m = k - 1;
    vector<vector<ll>> Lsub(m, vector<ll>(m, 0));
    for (int i = 1; i < k; i++){
        for (int j = 1; j < k; j++){
            Lsub[i-1][j-1] = L[i][j];
        }
    }
    vector<vector<ll>> invL = matInv(Lsub);
    if(invL.empty()) return 0;

    ll effSum = 0;
    for (int j = 1; j < k; j++){
        effSum = (effSum + invL[j-1][j-1]) % MOD;
    }

    for (int i = 1; i < k; i++){
        for (int j = i+1; j < k; j++){
            ll r_ij = (invL[i-1][i-1] + invL[j-1][j-1] - 2 * invL[i-1][j-1]) % MOD;
            if(r_ij < 0) r_ij += MOD;
            effSum = (effSum + r_ij) % MOD;
        }
    }
    ll ret = (tau * effSum) % MOD;
    return ret;
}
 
struct Edge { int u, v; ll w; };
 
int main() {
    int N = Cin(), M = Cin();
    vector<Edge> edges(M);
    vector<vector<ll>> fullAdj(N, vector<ll>(N, 0));
    vector<ll> candWeights;
    vector<vector<pair<int,ll>>> adj(N);
    for (int i = 0; i < M; i++){
        int u = Cin()-1, v = Cin()-1;
        ll d = Cin();
        edges[i] = {u, v, d};
        fullAdj[u][v] = 1; 
        fullAdj[v][u] = 1;
        candWeights.push_back(d);
        adj[u].push_back({v,d});
        adj[v].push_back({u,d});
    }
    sort(candWeights.begin(), candWeights.end());
    candWeights.erase(unique(candWeights.begin(), candWeights.end()), candWeights.end());
    sort(candWeights.begin(), candWeights.end(), greater<ll>());
 
    bool found = false;
    ll best_d = 0, bestCount = 0;
    for (ll dCand : candWeights) {
        vector<vector<int>> Hmat(N, vector<int>(N, 0));
        for (int i = 0; i < N; i++){
            for (int j = 0; j < N; j++){
                bool include = false;
                for (auto &p : adj[i]){
                    if(p.first == j && p.second < dCand) { include = true; break; }
                }
                Hmat[i][j] = include ? 1 : 0;
            }
        }
        vector<int> comp(N, -1);
        int compCount = 0;
        for (int i = 0; i < N; i++){
            if(comp[i] == -1){
                queue<int>q;
                q.push(i);
                comp[i] = compCount;
                while(!q.empty()){
                    int cur = q.front(); q.pop();
                    for (int nb = 0; nb < N; nb++){
                        if(Hmat[cur][nb] && comp[nb]==-1){
                            comp[nb] = compCount;
                            q.push(nb);
                        }
                    }
                }
                compCount++;
            }
        }
        if(compCount < 2) continue;
 
        vector<vector<int>> compVerts(compCount);
        for (int i = 0; i < N; i++){
            compVerts[comp[i]].push_back(i);
        }
 
        ll Mprod = 1;
        for (int i = 0; i < compCount; i++){
            int sz = compVerts[i].size();
            vector<vector<ll>> subMat(sz, vector<ll>(sz,0));
            for (int a = 0; a < sz; a++){
                for (int b = 0; b < sz; b++){
                    int va = compVerts[i][a], vb = compVerts[i][b];
                    subMat[a][b] = fullAdj[va][vb];
                }
            }
            ll treeCount = spanningTreeCount(subMat);
            Mprod = (Mprod * treeCount) % MOD;
        }
 
        vector<vector<ll>> J(compCount, vector<ll>(compCount, 0));
        vector<vector<ll>> Jprime(compCount, vector<ll>(compCount, 0));
        for (int i = 0; i < compCount; i++){
            for (int j = i+1; j < compCount; j++){
                ll cnt = 0, cntP = 0;
                for (int u : compVerts[i]){
                    for (auto &p : adj[u]){
                        int v = p.first; ll w = p.second;
                        if(comp[v]==j){
                            if(w >= dCand) cnt++;
                            if(w > dCand) cntP++;
                        }
                    }
                }
                J[i][j] = J[j][i] = cnt % MOD;
                Jprime[i][j] = Jprime[j][i] = cntP % MOD;
            }
        }
 
        ll F2J = computeF2_ordered(J);
        ll F2Jprime = computeF2_ordered(Jprime);
        ll diff = (F2J - F2Jprime) % MOD;
        if(diff < 0) diff += MOD;
 
        ll curCount = (2 * Mprod) % MOD;
        curCount = (curCount * diff) % MOD;
 
        if(curCount % MOD != 0) {
            best_d = dCand;
            bestCount = curCount;
            found = true;
            break;
        }
    }
    if(!found) Couts((char*)"0 0");
    else Cout(best_d), pc(' '), Cout(bestCount % MOD);
    pc('\n');
    return 0;
}
accuracy: 0.06666666666666667
time_ms: 330
memory_kb: 4160
final_rank: 20

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



enum Type{
	BASE,
	WORK,
};

typedef vector<ll> V;
typedef vector<V> MATRIX;

#define SIZE 105

struct Edge{

	bool operator<(const struct Edge& arg) const{

		return dist < arg.dist;
	}
	int a,b,dist;
};

int N,E,POW[7];
int boss[SIZE][2],height[SIZE][2],num_member[SIZE][2];
bool table[SIZE][SIZE][2];
Edge edge[SIZE*SIZE];


ll gcd(ll x,ll y){

	x = abs(x);
	y = abs(y);

	if(x < y){
		swap(x,y);
	}
	if(y == 0){
		return x;
	}else{
		return gcd(y,x%y);
	}
}

ll lcm(ll x,ll y){

	return (x*y)/gcd(x,y);
}

ll extgcd(ll a,ll b,ll &x,ll &y){
	ll d = a;
	if(b != 0){
		d = extgcd(b,a%b,y,x);
		y -= (a/b)*x;
	}else{
		x = 1;
		y = 0;
	}
	return d;
}

ll mod_inverse(ll a,ll m){
    ll x,y;
    extgcd(a,m,x,y);
    return (m+x%m)%m;
}


ll determinant(MATRIX A,int M){

	MATRIX C(M,V(M));

	for(int row = 0; row < M; row++){
		for(int col = 0; col < M; col++){
			C[row][col] = (A[row][col]+MOD)%MOD;
		}
	}

	ll mult = 1;

	for(int base = 0; base < M-1; base++){

		int row = -1;
		for(int i = base; i < M; i++){
			if(C[i][base] != 0){

				row = i;
				break;
			}
		}

		if(row == -1)return 0;

		if(row != base){

			swap(C[row],C[base]);
			mult *= (-1+MOD)%MOD;
			mult %= MOD;
		}

		for(int i= base+1; i < M; i++){

			ll tmp = (C[i][base]*mod_inverse(C[base][base],MOD))%MOD;
			for(int k = base; k < M; k++){
				C[i][k] -= (tmp*C[base][k])%MOD;
				if(C[i][k] < 0){
					C[i][k] = abs(C[i][k])%MOD;
					C[i][k] *= -1;
					C[i][k] += MOD;
				}
			}
		}
	}

	ll ret = 1;
	for(int i = 0; i < M; i++){

		ret *= C[i][i];
		ret %= MOD;
	}

	ret *= mod_inverse(mult,MOD);

	return ret%MOD;
}



int get_boss(int id,Type type){
	if(boss[id][type] == id)return id;
	else{
		return boss[id][type] = get_boss(boss[id][type],type);
	}
}

int isSame(int x,int y,Type type){
	return get_boss(x,type) == get_boss(y,type);
}

void unite(int x,int y,Type type){
	int boss_x = get_boss(x,type);
	int boss_y = get_boss(y,type);

	if(boss_x == boss_y)return;

	if(height[boss_x][type] > height[boss_y][type]){

		num_member[boss_x][type] += num_member[boss_y][type];
		boss[boss_y][type] = boss_x;

	}else if(height[boss_x][type] < height[boss_y][type]){

		num_member[boss_y][type] += num_member[boss_x][type];
		boss[boss_x][type] = boss_y;

	}else{ //height[boss_x][type] == height[boss_y][type]

		num_member[boss_x][type] += num_member[boss_y][type];
		boss[boss_y][type] = boss_x;
		height[x][type]++;
	}
}



ll calc_Laplacian(vector<int> tmp_vec){

	if(tmp_vec.size() == 1){

		return 1;
	}

	int M = tmp_vec.size()-1;

	MATRIX MULT(M,V(M));
	for(int row = 0; row < M; row++){
		for(int col = 0; col < M; col++){

			MULT[row][col] = 0;
		}
	}

	//次数を求める
	for(int i = 0; i < M; i++){
		int num = 0;
		for(int k = 0; k < tmp_vec.size(); k++){ //★★★ループ上限に注意★★★
			if(k == i)continue;

			if(table[tmp_vec[i]][tmp_vec[k]][WORK]){

				num++;
			}
		}
		MULT[i][i] = num;
	}

	for(int i = 0; i < M; i++){
		for(int k = 0; k < M; k++){
			if(k == i)continue;

			if(table[tmp_vec[i]][tmp_vec[k]][WORK]){

				MULT[i][k] = -1;
			}
		}
	}

	return determinant(MULT,M);
}

int main(){

	POW[0] = 1;
	for(int i = 1; i <= 6; i++){

		POW[i] = POW[i-1]*2;
	}

	scanf("%d %d",&N,&E);

	for(int i = 0; i < N; i++){

		boss[i][BASE] = i;
		height[i][BASE] = 0;
		num_member[i][BASE] = 1;

		for(int k = 0; k < N; k++){

			table[i][k][BASE] = false;
		}
	}

	for(int i = 0; i < E; i++){

		scanf("%d %d %d",&edge[i].a,&edge[i].b,&edge[i].dist);
		edge[i].a--;
		edge[i].b--;
	}

	sort(edge,edge+E);

	int base_dist = -1;

	for(int i = 0; i < E; i++){
		if(isSame(edge[i].a,edge[i].b,BASE))continue;

		unite(edge[i].a,edge[i].b,BASE);
		if(num_member[get_boss(edge[i].a,BASE)][BASE] == N){

			base_dist = edge[i].dist;
			break;
		}
	}

	for(int i = 0; i < N; i++){

		boss[i][BASE] = i;
		height[i][BASE] = 0;
		num_member[i][BASE] = 1;
	}

	vector<int> vec;

	//base_dist未満の重みのエッジをuniteする
	for(int i = 0; i < E; i++){

		if(edge[i].dist >= base_dist){ //★★★注意:基準長より長い辺も、全域木の場合の数計算のため、未確定辺集合に追加する★★★

			vec.push_back(i);
			continue;
		}

		unite(edge[i].a,edge[i].b,BASE);
		table[edge[i].a][edge[i].b][BASE] = true;
		table[edge[i].b][edge[i].a][BASE] = true;
	}

	vector<int> REST;
	for(int i = 0; i < N; i++){
		if(i == get_boss(i,BASE)){

			REST.push_back(i); //各連結成分のボス
		}
	}

	ll ans = 0;

	//Aに割り当てる連結成分を走査
	for(int state = 0; state < POW[REST.size()]; state++){

		for(int i = 0; i < N; i++){

			boss[i][WORK] = boss[i][BASE];
			height[i][WORK] = height[i][BASE];
			num_member[i][WORK] = num_member[i][BASE];

			for(int k = 0; k < N; k++){

				table[i][k][WORK] = table[i][k][BASE];
			}
		}

		vector<int> A,B;
		map<int,bool> MAP;

		for(int i = 0; i < REST.size(); i++){
			if(state&POW[i]){

				A.push_back(REST[i]);
				MAP[REST[i]] = true;

			}else{

				B.push_back(REST[i]);
				MAP[REST[i]] = false;
			}
		}

		if(A.size() == 0 || B.size() == 0)continue;

		for(int i = 0; i < vec.size(); i++){

			int a = get_boss(edge[vec[i]].a,WORK);
			int b = get_boss(edge[vec[i]].b,WORK);

			if(MAP[a] == MAP[b]){ //同じグループ

				unite(edge[vec[i]].a,edge[vec[i]].b,WORK);
				table[edge[vec[i]].a][edge[vec[i]].b][WORK] = true;
				table[edge[vec[i]].b][edge[vec[i]].a][WORK] = true;
			}
		}

		int BOSS_1 = -1,BOSS_2 = -1;
		for(int i = 0; i < REST.size(); i++){
			if(state&POW[i]){
				if(BOSS_1 == -1){

					BOSS_1 = get_boss(REST[i],WORK);
				}

			}else{
				if(BOSS_2 == -1){

					BOSS_2 = get_boss(REST[i],WORK);
				}
			}
		}

		vector<int> tmp_vec[2];

		for(int i = 0; i < N; i++){
			if(get_boss(i,WORK) == BOSS_1){

				tmp_vec[0].push_back(i);
			}else{

				tmp_vec[1].push_back(i);
			}
		}

		ll num_1 = calc_Laplacian(tmp_vec[0]);
		ll num_2 = calc_Laplacian(tmp_vec[1]);

		ans += (num_1*num_2)%MOD;
		ans %= MOD;
	}

	printf("%d %lld\n",base_dist,ans);

	return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 3432
final_rank: 4

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



enum Type{
	BASE,
	WORK,
};

typedef vector<ll> V;
typedef vector<V> MATRIX;

#define SIZE 105

struct Edge{

	bool operator<(const struct Edge& arg) const{

		return dist < arg.dist;
	}
	int a,b,dist;
};

int N,E,POW[7];
int boss[SIZE][2],height[SIZE][2],num_member[SIZE][2];
bool table[SIZE][SIZE][2];
Edge edge[SIZE*SIZE];


ll gcd(ll x,ll y){

	x = abs(x);
	y = abs(y);

	if(x < y){
		swap(x,y);
	}
	if(y == 0){
		return x;
	}else{
		return gcd(y,x%y);
	}
}

ll lcm(ll x,ll y){

	return (x*y)/gcd(x,y);
}

ll extgcd(ll a,ll b,ll &x,ll &y){
	ll d = a;
	if(b != 0){
		d = extgcd(b,a%b,y,x);
		y -= (a/b)*x;
	}else{
		x = 1;
		y = 0;
	}
	return d;
}

ll mod_inverse(ll a,ll m){
    ll x,y;
    extgcd(a,m,x,y);
    return (m+x%m)%m;
}


ll determinant(MATRIX A,int M){

	MATRIX C(M,V(M));

	for(int row = 0; row < M; row++){
		for(int col = 0; col < M; col++){
			C[row][col] = (A[row][col]+MOD)%MOD;
		}
	}

	ll mult = 1;

	for(int base = 0; base < M-1; base++){

		int row = -1;
		for(int i = base; i < M; i++){
			if(C[i][base] != 0){

				row = i;
				break;
			}
		}

		if(row == -1)return 0;

		if(row != base){

			swap(C[row],C[base]);
			mult *= (-1+MOD)%MOD;
			mult %= MOD;
		}

		for(int i= base+1; i < M; i++){

			//C[i][base]が0でもSKIPするとWAになる場合あり(AOJ 2347 Sunny Graph)

			ll tmp = (C[i][base]*mod_inverse(C[base][base],MOD))%MOD;
			for(int k = base; k < M; k++){
				C[i][k] -= (tmp*C[base][k])%MOD;
				if(C[i][k] < 0){
					C[i][k] = abs(C[i][k])%MOD;
					C[i][k] *= -1;
					C[i][k] += MOD;

					if(C[i][k] < 0 || C[i][k] >= MOD){

						printf("バグ!!!");
						return -1;
					}
				}
			}
		}
	}

	ll ret = 1;
	for(int i = 0; i < M; i++){

		ret *= C[i][i];
		ret %= MOD;
	}

	ret *= mod_inverse(mult,MOD);

	return ret%MOD;
}



int get_boss(int id,Type type){
	if(boss[id][type] == id)return id;
	else{
		return boss[id][type] = get_boss(boss[id][type],type);
	}
}

int isSame(int x,int y,Type type){
	return get_boss(x,type) == get_boss(y,type);
}

void unite(int x,int y,Type type){
	int boss_x = get_boss(x,type);
	int boss_y = get_boss(y,type);

	if(boss_x == boss_y)return;

	if(height[boss_x][type] > height[boss_y][type]){

		num_member[boss_x][type] += num_member[boss_y][type];
		boss[boss_y][type] = boss_x;

	}else if(height[boss_x][type] < height[boss_y][type]){

		num_member[boss_y][type] += num_member[boss_x][type];
		boss[boss_x][type] = boss_y;

	}else{ //height[boss_x][type] == height[boss_y][type]

		num_member[boss_x][type] += num_member[boss_y][type];
		boss[boss_y][type] = boss_x;
		height[x][type]++;
	}
}



ll calc_Laplacian(vector<int> tmp_vec){

	if(tmp_vec.size() == 1){

		return 1;
	}

	int M = tmp_vec.size()-1;

	MATRIX MULT(M,V(M));
	for(int row = 0; row < M; row++){
		for(int col = 0; col < M; col++){

			MULT[row][col] = 0;
		}
	}

	//次数を求める
	for(int i = 0; i < M; i++){
		int num = 0;
		for(int k = 0; k < tmp_vec.size(); k++){ //★★★ループ上限に注意★★★
			if(k == i)continue;

			if(table[tmp_vec[i]][tmp_vec[k]][WORK]){

				num++;
			}
		}
		MULT[i][i] = num;
	}

	for(int i = 0; i < M; i++){
		for(int k = 0; k < M; k++){
			if(k == i)continue;

			if(table[tmp_vec[i]][tmp_vec[k]][WORK]){

				MULT[i][k] = -1;
			}
		}
	}

	return determinant(MULT,M);
}

int main(){

	POW[0] = 1;
	for(int i = 1; i <= 6; i++){

		POW[i] = POW[i-1]*2;
	}

	scanf("%d %d",&N,&E);

	for(int i = 0; i < N; i++){

		boss[i][BASE] = i;
		height[i][BASE] = 0;
		num_member[i][BASE] = 1;

		for(int k = 0; k < N; k++){

			table[i][k][BASE] = false;
		}
	}

	for(int i = 0; i < E; i++){

		scanf("%d %d %d",&edge[i].a,&edge[i].b,&edge[i].dist);
		edge[i].a--;
		edge[i].b--;
	}

	sort(edge,edge+E);

	int base_dist = -1;

	for(int i = 0; i < E; i++){
		if(isSame(edge[i].a,edge[i].b,BASE))continue;

		unite(edge[i].a,edge[i].b,BASE);
		if(num_member[get_boss(edge[i].a,BASE)][BASE] == N){

			base_dist = edge[i].dist;
			break;
		}
	}

	for(int i = 0; i < N; i++){

		boss[i][BASE] = i;
		height[i][BASE] = 0;
		num_member[i][BASE] = 1;
	}

	vector<int> vec;

	//base_dist未満の重みのエッジをuniteする
	for(int i = 0; i < E; i++){

		if(edge[i].dist >= base_dist){ //★★★注意:基準長より長い辺も、全域木の場合の数計算のため、未確定辺集合に追加する★★★

			vec.push_back(i);
			continue;
		}

		unite(edge[i].a,edge[i].b,BASE);
		table[edge[i].a][edge[i].b][BASE] = true;
		table[edge[i].b][edge[i].a][BASE] = true;
	}

	vector<int> REST;
	for(int i = 0; i < N; i++){
		if(i == get_boss(i,BASE)){

			REST.push_back(i); //各連結成分のボス
		}
	}

	ll ans = 0;

	//Aに割り当てる連結成分を走査
	for(int state = 0; state < POW[REST.size()]; state++){

		for(int i = 0; i < N; i++){

			boss[i][WORK] = boss[i][BASE];
			height[i][WORK] = height[i][BASE];
			num_member[i][WORK] = num_member[i][BASE];

			for(int k = 0; k < N; k++){

				table[i][k][WORK] = table[i][k][BASE];
			}
		}

		vector<int> A,B;
		map<int,bool> MAP;

		for(int i = 0; i < REST.size(); i++){
			if(state&POW[i]){

				A.push_back(REST[i]);
				MAP[REST[i]] = true;

			}else{

				B.push_back(REST[i]);
				MAP[REST[i]] = false;
			}
		}

		if(A.size() == 0 || B.size() == 0)continue;

		for(int i = 0; i < vec.size(); i++){

			int a = get_boss(edge[vec[i]].a,WORK);
			int b = get_boss(edge[vec[i]].b,WORK);

			if(MAP[a] == MAP[b]){ //同じグループ

				unite(edge[vec[i]].a,edge[vec[i]].b,WORK);
				table[edge[vec[i]].a][edge[vec[i]].b][WORK] = true;
				table[edge[vec[i]].b][edge[vec[i]].a][WORK] = true;
			}
		}

		int BOSS_1 = -1,BOSS_2 = -1;
		for(int i = 0; i < REST.size(); i++){
			if(state&POW[i]){
				if(BOSS_1 == -1){

					BOSS_1 = get_boss(REST[i],WORK);
				}

			}else{
				if(BOSS_2 == -1){

					BOSS_2 = get_boss(REST[i],WORK);
				}
			}
		}

		vector<int> tmp_vec[2];

		for(int i = 0; i < N; i++){
			if(get_boss(i,WORK) == BOSS_1){

				tmp_vec[0].push_back(i);
			}else{

				tmp_vec[1].push_back(i);
			}
		}

		ll num_1 = calc_Laplacian(tmp_vec[0]);
		ll num_2 = calc_Laplacian(tmp_vec[1]);

		ans += (num_1*num_2)%MOD;
		ans %= MOD;
	}

	printf("%d %lld\n",base_dist,ans);

	return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 3464
final_rank: 5

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



enum Type{
	BASE,
	WORK,
};

typedef vector<ll> V;
typedef vector<V> MATRIX;

#define SIZE 105

struct Edge{

	bool operator<(const struct Edge& arg) const{

		return dist < arg.dist;
	}
	int a,b,dist;
};

int N,E,POW[7];
int boss[SIZE][2],height[SIZE][2],num_member[SIZE][2];
bool table[SIZE][SIZE][2];
Edge edge[SIZE*SIZE];


ll gcd(ll x,ll y){

	x = abs(x);
	y = abs(y);

	if(x < y){
		swap(x,y);
	}
	if(y == 0){
		return x;
	}else{
		return gcd(y,x%y);
	}
}

ll lcm(ll x,ll y){

	return (x*y)/gcd(x,y);
}

ll extgcd(ll a,ll b,ll &x,ll &y){
	ll d = a;
	if(b != 0){
		d = extgcd(b,a%b,y,x);
		y -= (a/b)*x;
	}else{
		x = 1;
		y = 0;
	}
	return d;
}

ll mod_inverse(ll a,ll m){
    ll x,y;
    extgcd(a,m,x,y);
    return (m+x%m)%m;
}

bool DEBUG = false;

ll determinant(MATRIX A,int M){

	MATRIX C(M,V(M));

	for(int row = 0; row < M; row++){
		for(int col = 0; col < M; col++){
			C[row][col] = (A[row][col]+MOD)%MOD;
		}
	}

	ll mult = 1;

	for(int base = 0; base < M-1; base++){

		int row = -1;
		for(int i = base; i < M; i++){
			if(C[i][base] != 0){

				row = i;
				break;
			}
		}

		if(row == -1)return 0;

		if(row != base){

			swap(C[row],C[base]);
			mult *= (-1+MOD)%MOD;
			mult %= MOD;
		}

		for(int i= base+1; i < M; i++){

			//C[i][base]が0でもSKIPするとWAになる場合あり(AOJ 2347 Sunny Graph)

			ll tmp = (C[i][base]*mod_inverse(C[base][base],MOD))%MOD;
			for(int k = base; k < M; k++){
				C[i][k] -= (tmp*C[base][k])%MOD;
				if(C[i][k] < 0){
					C[i][k] = abs(C[i][k])%MOD;
					C[i][k] *= -1;
					C[i][k] += MOD;

					if(C[i][k] < 0 || C[i][k] >= MOD){

						printf("バグ!!!");
						return -1;
					}
				}
			}
		}
	}

	ll ret = 1;
	for(int i = 0; i < M; i++){

		ret *= C[i][i];
		ret %= MOD;
	}

	ret *= mod_inverse(mult,MOD);

	return ret%MOD;
}



int get_boss(int id,Type type){
	if(boss[id][type] == id)return id;
	else{
		return boss[id][type] = get_boss(boss[id][type],type);
	}
}

int isSame(int x,int y,Type type){
	return get_boss(x,type) == get_boss(y,type);
}

void unite(int x,int y,Type type){
	int boss_x = get_boss(x,type);
	int boss_y = get_boss(y,type);

	if(boss_x == boss_y)return;

	if(height[boss_x][type] > height[boss_y][type]){

		num_member[boss_x][type] += num_member[boss_y][type];
		boss[boss_y][type] = boss_x;

	}else if(height[boss_x][type] < height[boss_y][type]){

		num_member[boss_y][type] += num_member[boss_x][type];
		boss[boss_x][type] = boss_y;

	}else{ //height[boss_x][type] == height[boss_y][type]

		num_member[boss_x][type] += num_member[boss_y][type];
		boss[boss_y][type] = boss_x;
		height[x][type]++;
	}
}



ll calc_Laplacian(vector<int> tmp_vec){

	if(tmp_vec.size() == 1){

		return 1;
	}

	int M = tmp_vec.size()-1;

	MATRIX MULT(M,V(M));
	for(int row = 0; row < M; row++){
		for(int col = 0; col < M; col++){

			MULT[row][col] = 0;
		}
	}

	//次数を求める
	for(int i = 0; i < M; i++){
		int num = 0;
		for(int k = 0; k < tmp_vec.size(); k++){ //★★★ループ上限に注意★★★
			if(k == i)continue;

			if(table[tmp_vec[i]][tmp_vec[k]][WORK]){

				num++;
			}
		}
		MULT[i][i] = num;
	}

	for(int i = 0; i < M; i++){
		for(int k = 0; k < M; k++){
			if(k == i)continue;

			if(table[tmp_vec[i]][tmp_vec[k]][WORK]){

				MULT[i][k] = -1;
			}
		}
	}

	if(DEBUG){

		printf("\n");
		for(int row = 0; row < M; row++){
			for(int col = 0; col < M; col++){

				printf(" %lld",MULT[row][col]);
			}
			printf("\n");
		}
	}

	return determinant(MULT,M);
}

int main(){

	POW[0] = 1;
	for(int i = 1; i <= 6; i++){

		POW[i] = POW[i-1]*2;
	}

	scanf("%d %d",&N,&E);

	for(int i = 0; i < N; i++){

		boss[i][BASE] = i;
		height[i][BASE] = 0;
		num_member[i][BASE] = 1;

		for(int k = 0; k < N; k++){

			table[i][k][BASE] = false;
		}
	}

	for(int i = 0; i < E; i++){

		scanf("%d %d %d",&edge[i].a,&edge[i].b,&edge[i].dist);
		edge[i].a--;
		edge[i].b--;
	}

	sort(edge,edge+E);

	int base_dist = -1;

	for(int i = 0; i < E; i++){
		if(isSame(edge[i].a,edge[i].b,BASE))continue;

		unite(edge[i].a,edge[i].b,BASE);
		if(num_member[get_boss(edge[i].a,BASE)][BASE] == N){

			base_dist = edge[i].dist;
			break;
		}
	}

	if(DEBUG){

		printf("base_dist:%d\n",base_dist);
	}



	for(int i = 0; i < N; i++){

		boss[i][BASE] = i;
		height[i][BASE] = 0;
		num_member[i][BASE] = 1;
	}

	vector<int> vec;

	//base_dist未満の重みのエッジをuniteする
	for(int i = 0; i < E; i++){
		//if(edge[i].dist > base_dist)break;

		if(edge[i].dist >= base_dist){


			vec.push_back(i);
			continue;
		}

		unite(edge[i].a,edge[i].b,BASE);
		table[edge[i].a][edge[i].b][BASE] = true;
		table[edge[i].b][edge[i].a][BASE] = true;
	}

	vector<int> REST;
	for(int i = 0; i < N; i++){
		if(i == get_boss(i,BASE)){

			REST.push_back(i); //各連結成分のボス
		}
	}

	if(DEBUG){
		printf("REST.size():%lld\n",REST.size());
		for(int i = 0; i < REST.size(); i++){

			printf("%d\n",REST[i]);
		}
		printf("vec.size():%lld\n",vec.size());
		for(int i = 0; i < vec.size(); i++){

			printf("%d\n",vec[i]);
		}
	}

	ll ans = 0;

	//Aに割り当てる連結成分を走査
	for(int state = 0; state < POW[REST.size()]; state++){

		for(int i = 0; i < N; i++){

			boss[i][WORK] = boss[i][BASE];
			height[i][WORK] = height[i][BASE];
			num_member[i][WORK] = num_member[i][BASE];

			for(int k = 0; k < N; k++){

				table[i][k][WORK] = table[i][k][BASE];
			}
		}

		vector<int> A,B;
		map<int,bool> MAP;

		for(int i = 0; i < REST.size(); i++){
			if(state&POW[i]){

				A.push_back(REST[i]);
				MAP[REST[i]] = true;

			}else{

				B.push_back(REST[i]);
				MAP[REST[i]] = false;
			}
		}

		if(A.size() == 0 || B.size() == 0)continue;

		for(int i = 0; i < vec.size(); i++){

			int a = get_boss(edge[vec[i]].a,WORK);
			int b = get_boss(edge[vec[i]].b,WORK);

			if(MAP[a] == MAP[b]){ //同じグループ

				if(DEBUG){

				printf("同じグループ\n");
				}

				unite(edge[vec[i]].a,edge[vec[i]].b,WORK);
				table[edge[vec[i]].a][edge[vec[i]].b][WORK] = true;
				table[edge[vec[i]].b][edge[vec[i]].a][WORK] = true;
			}
		}

		int BOSS_1 = -1,BOSS_2 = -1;
		for(int i = 0; i < REST.size(); i++){
			if(state&POW[i]){
				if(BOSS_1 == -1){

					BOSS_1 = get_boss(REST[i],WORK);
				}

			}else{
				if(BOSS_2 == -1){

					BOSS_2 = get_boss(REST[i],WORK);
				}
			}
		}

		vector<int> tmp_vec[2];

		for(int i = 0; i < N; i++){
			if(get_boss(i,WORK) == BOSS_1){

				if(DEBUG){

					printf("%dがグループ0\n",i);
				}

				tmp_vec[0].push_back(i);
			}else{

				tmp_vec[1].push_back(i);
			}
		}

		ll num_1 = calc_Laplacian(tmp_vec[0]);
		ll num_2 = calc_Laplacian(tmp_vec[1]);

		if(DEBUG){
		printf("num1:%lld num2:%lld\n",num_1,num_2);
		}

		ans += (num_1*num_2)%MOD;
		ans %= MOD;
	}

	printf("%d %lld\n",base_dist,ans);

	return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 3468
final_rank: 6

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



enum Type{
	BASE,
	WORK,
};

typedef vector<ll> V;
typedef vector<V> MATRIX;

#define SIZE 105

struct Edge{

	bool operator<(const struct Edge& arg) const{

		return dist < arg.dist;
	}
	int a,b,dist;
};

int N,E,POW[7];
int boss[SIZE][2],height[SIZE][2],num_member[SIZE][2];
bool table[SIZE][SIZE][2];
Edge edge[SIZE*SIZE];


ll gcd(ll x,ll y){

	x = abs(x);
	y = abs(y);

	if(x < y){
		swap(x,y);
	}
	if(y == 0){
		return x;
	}else{
		return gcd(y,x%y);
	}
}

ll lcm(ll x,ll y){

	return (x*y)/gcd(x,y);
}

ll extgcd(ll a,ll b,ll &x,ll &y){
	ll d = a;
	if(b != 0){
		d = extgcd(b,a%b,y,x);
		y -= (a/b)*x;
	}else{
		x = 1;
		y = 0;
	}
	return d;
}

ll mod_inverse(ll a,ll m){
    ll x,y;
    extgcd(a,m,x,y);
    return (m+x%m)%m;
}

bool DEBUG = false;

ll determinant(MATRIX A,int M){

	MATRIX C(M,V(M));

	for(int row = 0; row < M; row++){
		for(int col = 0; col < M; col++){
			C[row][col] = (A[row][col]+MOD)%MOD;
		}
	}

	ll mult = 1;

	for(int base = 0; base < M-1; base++){

		int row = -1;
		for(int i = base; i < M; i++){
			if(C[i][base] != 0){

				row = i;
				break;
			}
		}

		if(row == -1)return 0;

		if(row != base){

			swap(C[row],C[base]);
			mult *= (-1+MOD)%MOD;
			mult %= MOD;
		}

		for(int i= base+1; i < M; i++){

			//C[i][base]が0でもSKIPするとWAになる場合あり(AOJ 2347 Sunny Graph)

			ll tmp = C[i][base]*mod_inverse(C[base][base],MOD)%MOD;
			for(int k = base; k < M; k++){
				C[i][k] -= tmp*C[base][k]%MOD;
				if(C[i][k] < 0){
					C[i][k] = abs(C[i][k])%MOD;
					C[i][k] *= -1;
					C[i][k] += MOD;
				}
			}
		}
	}

	ll ret = 1;
	for(int i = 0; i < M; i++){

		ret *= C[i][i];
		ret %= MOD;
	}

	ret *= mod_inverse(mult,MOD);

	return ret%MOD;
}



int get_boss(int id,Type type){
	if(boss[id][type] == id)return id;
	else{
		return boss[id][type] = get_boss(boss[id][type],type);
	}
}

int isSame(int x,int y,Type type){
	return get_boss(x,type) == get_boss(y,type);
}

void unite(int x,int y,Type type){
	int boss_x = get_boss(x,type);
	int boss_y = get_boss(y,type);

	if(boss_x == boss_y)return;

	if(height[boss_x][type] > height[boss_y][type]){

		num_member[boss_x][type] += num_member[boss_y][type];
		boss[boss_y][type] = boss_x;

	}else if(height[boss_x][type] < height[boss_y][type]){

		num_member[boss_y][type] += num_member[boss_x][type];
		boss[boss_x][type] = boss_y;

	}else{ //height[boss_x][type] == height[boss_y][type]

		num_member[boss_x][type] += num_member[boss_y][type];
		boss[boss_y][type] = boss_x;
		height[x][type]++;
	}
}



ll calc_Laplacian(vector<int> tmp_vec){

	int M = tmp_vec.size()-1;

	MATRIX MULT(M,V(M));
	for(int row = 0; row < M; row++){
		for(int col = 0; col < M; col++){

			MULT[row][col] = 0;
		}
	}

	//次数を求める
	for(int i = 0; i < M; i++){
		int num = 0;
		for(int k = 0; k < tmp_vec.size(); k++){ //★★★ループ上限に注意★★★
			if(k == i)continue;

			if(table[tmp_vec[i]][tmp_vec[k]][WORK]){

				num++;
			}
		}
		MULT[i][i] = num;
	}

	for(int i = 0; i < M; i++){
		for(int k = 0; k < M; k++){
			if(k == i)continue;

			if(table[tmp_vec[i]][tmp_vec[k]][WORK]){

				MULT[i][k] = -1;
			}
		}
	}

	if(DEBUG){

		printf("\n");
		for(int row = 0; row < M; row++){
			for(int col = 0; col < M; col++){

				printf(" %lld",MULT[row][col]);
			}
			printf("\n");
		}
	}

	return determinant(MULT,M);
}

int main(){

	POW[0] = 1;
	for(int i = 1; i <= 6; i++){

		POW[i] = POW[i-1]*2;
	}

	scanf("%d %d",&N,&E);

	for(int i = 0; i < N; i++){

		boss[i][BASE] = i;
		height[i][BASE] = 0;
		num_member[i][BASE] = 1;

		for(int k = 0; k < N; k++){

			table[i][k][BASE] = false;
		}
	}

	for(int i = 0; i < E; i++){

		scanf("%d %d %d",&edge[i].a,&edge[i].b,&edge[i].dist);
		edge[i].a--;
		edge[i].b--;
	}

	sort(edge,edge+E);

	int base_dist = -1;

	for(int i = 0; i < E; i++){
		if(isSame(edge[i].a,edge[i].b,BASE))continue;

		unite(edge[i].a,edge[i].b,BASE);
		if(num_member[get_boss(edge[i].a,BASE)][BASE] == N){

			base_dist = edge[i].dist;
			break;
		}
	}

	if(DEBUG){

		printf("base_dist:%d\n",base_dist);
	}

	vector<int> vec;
	for(int i = 0; i < E; i++){
		if(edge[i].dist == base_dist){

			vec.push_back(i);
		}
	}

	for(int i = 0; i < N; i++){

		boss[i][BASE] = i;
		height[i][BASE] = 0;
		num_member[i][BASE] = 1;
	}

	//base_dist未満の重みのエッジをuniteする
	for(int i = 0; i < E; i++){
		if(edge[i].dist >= base_dist)break;

		unite(edge[i].a,edge[i].b,BASE);
		table[edge[i].a][edge[i].b][BASE] = true;
		table[edge[i].b][edge[i].a][BASE] = true;
	}

	vector<int> REST;
	for(int i = 0; i < N; i++){
		if(i == get_boss(i,BASE)){

			REST.push_back(i); //各連結成分のボス
		}
	}

	if(DEBUG){
		printf("REST.size():%lld\n",REST.size());
		for(int i = 0; i < REST.size(); i++){

			printf("%d\n",REST[i]);
		}
		printf("vec.size():%lld\n",vec.size());
		for(int i = 0; i < vec.size(); i++){

			printf("%d\n",vec[i]);
		}
	}

	ll ans = 0;

	map<int,bool> MAP;

	for(int state = 0; state < POW[vec.size()]; state++){

		for(int i = 0; i < N; i++){

			boss[i][WORK] = boss[i][BASE];
			height[i][WORK] = height[i][BASE];
			num_member[i][WORK] = num_member[i][BASE];

			for(int k = 0; k < N; k++){

				table[i][k][WORK] = table[i][k][BASE];
			}
		}

		for(int loop = 0; loop < vec.size(); loop++){
			if(state&POW[loop]){

				int a = edge[vec[loop]].a;
				int b = edge[vec[loop]].b;

				unite(a,b,WORK);
				table[a][b][WORK] = true;
				table[b][a][WORK] = true;
			}
		}

		vector<int> BOSS;
		for(int i = 0; i < REST.size(); i++){
			if(REST[i] == get_boss(REST[i],WORK)){

				BOSS.push_back(REST[i]);
			}
		}

		if(BOSS.size() != 2)continue; //連結成分が2でなければSKIP

/*

		ll tmp_1 = 0,tmp_2 = 0;
		for(int i = 0; i < REST.size(); i++){

			if(isSame(REST[i],BOSS[0],WORK)){

				tmp_1 += POW[i];
			}else{

				tmp_2 += POW[i];
			}
		}

		auto at = MAP.find(tmp_1);
		if(at != MAP.end())continue; //既出の分割方法ならSKIP

		MAP[tmp_1] = true;
		MAP[tmp_2] = true; */


		vector<int> tmp_vec[2];

		for(int i = 0; i < N; i++){
			if(get_boss(i,WORK) == BOSS[0]){

				tmp_vec[0].push_back(i);
			}else{

				tmp_vec[1].push_back(i);
			}
		}

		ll num_1 = calc_Laplacian(tmp_vec[0]);
		ll num_2 = calc_Laplacian(tmp_vec[1]);

		if(DEBUG){
		printf("num1:%lld num2:%lld\n",num_1,num_2);
		}

		ans += (2*(num_1*num_2))%MOD;
		ans %= MOD;
	}

	printf("%d %lld\n",base_dist,ans);

	return 0;
}
accuracy: 0.06666666666666667
time_ms: 20
memory_kb: 3436
final_rank: 18

code_snippet: #include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <numeric>
#include <bitset>
#include <cmath>

static const int MOD = 1000000007;
using ll = long long;
using u32 = unsigned;
using u64 = unsigned long long;
using namespace std;

template<class T> constexpr T INF = ::numeric_limits<T>::max()/32*15+208;

struct QuickFind {
    int n, sz;
    vector<int> roots;
    vector<vector<int>> v;
    explicit QuickFind(int n) : n(n), sz(n) {
        v.resize(n);
        for (int i = 0; i < n; ++i) v[i].emplace_back(i);
        roots.resize(n);
        iota(roots.begin(),roots.end(), 0);
    }

    int root(int a){ return roots[a]; }
    int size(int a){ return v[a].size(); }
    int size(){ return sz; }
    bool unite(int a, int b){
        if(same(a, b)) return false;
        sz--;
        a = roots[a], b = roots[b];
        if(size(a) < size(b)) swap(a, b);
        for (auto &&i : v[b]) {
            v[a].emplace_back(i);
            roots[i] = a;
        }
        v[b].clear();
        v[b].shrink_to_fit();
        return true;
    }
    bool same(int a, int b){ return roots[a] == roots[b]; }
    const vector<int>& components(int x){ return v[roots[x]];}
};

template<u32 M = 1000000007>
struct modint{
    u32 val;
    modint(): val(0){}
    template<typename T>
    modint(T t){t %= (T)M; if(t < 0) t += (T)M; val = t;}

    modint pow(ll k) const {
        modint res(1), x(val);
        while(k){
            if(k&1) res *= x;
            x *= x;
            k >>= 1;
        }
        return res;
    }
    template<typename T>
    modint& operator=(T t){t %= (T)M; if(t < 0) t += (T)M; val = t; return *this;}
    modint inv() const {return pow(M-2);}
    modint& operator+=(modint a){val += a.val; if(val >= M) val -= M; return *this;}
    modint& operator-=(modint a){if(val < a.val) val += M-a.val; else val -= a.val; return *this;}
    modint& operator*=(modint a){val = (u64)val*a.val%M; return *this;}
    modint& operator/=(modint a){return (*this) *= a.inv();}
    modint operator+(modint a) const {return modint(val) +=a;}
    modint operator-(modint a) const {return modint(val) -=a;}
    modint operator*(modint a) const {return modint(val) *=a;}
    modint operator/(modint a) const {return modint(val) /=a;}
    modint operator-(){return modint(M-val);}
    bool operator==(const modint a) const {return val == a.val;}
    bool operator!=(const modint a) const {return val != a.val;}
    bool operator<(const modint a) const {return val < a.val;}
};
using mint = modint<MOD>;

template<class H>
struct matrix {
    using T = typename H::T;
    vector<vector<T>> A;
    matrix() = default;
    matrix(size_t n, size_t m) : A(n, vector<T>(m)) {}
    explicit matrix(size_t n) : A(n, vector<T> (n)) {};
    size_t height() const { return (A.size()); }
    size_t width() const { return (A[0].size()); }

    const vector<T> &operator [] (int k) const { return A[k]; }
    vector<T> &operator[] (int k) { return A[k]; }

    static matrix I(size_t n){
        matrix mat(n);
        for (int i = 0; i < n; ++i) mat[i][i] = 1;
        return mat;
    }

    matrix &operator+= (const matrix &B){
        size_t h = height(), w = width();
        for (int i = 0; i < h; ++i) {
            for (int j = 0; j < w; ++j) {
                add((*this)[i][j], B[i][j]);
            }
        }
    }

    matrix &operator-= (const matrix &B){
        size_t h = height(), w = width();
        for (int i = 0; i < h; ++i) {
            for (int j = 0; j < w; ++j) {
                add((*this)[i][j], -B[i][j]);
            }
        }
    }

    matrix &operator*=(const matrix &B) {
        size_t n = height(), m = B.width(), p = width();
        matrix C(n, m);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                for (int k = 0; k < p; ++k) {
                    add(C[i][j], mul((*this)[i][k],B[k][j]));
                }
            }
        }
        A.swap(C.A);
        return (*this);
    }

    matrix pow(ll n) const {
        matrix a = (*this), res = I(height());
        while(n > 0){
            if(n & 1) mul(res, a);
            mul(a, a);
            n >>= 1;
        }
        return res;
    }
    matrix operator+(const matrix &B) const {return matrix(*this) += B;}
    matrix operator-(const matrix &B) const {return matrix(*this) -= B;}
    matrix operator*(const matrix &B) const {return matrix(*this) *= B;}

    mint gauss_jordan(){
        mint res = 1;
        int rank = 0;
        for (int c = 0; c < width(); ++c) {
            int k = -1;
            for (int i = rank; i < height(); ++i) {
                if(A[i][c] != H::zero()){
                    k = i;
                    break;
                }
            }
            if(!~k) continue;
            swap(A[k], A[rank]);
            res *= A[rank][c];
            T x = T(1)/A[rank][c];
            for (int j = 0; j < width(); ++j) A[rank][j] *= x;
            for (int i = 0; i < height(); ++i) {
                if(i != rank && A[i][c] != H::zero()){
                    T coeff = A[i][c];
                    for (int j = 0; j < width(); ++j) {
                        A[i][j] -= A[rank][j]*coeff;
                    }
                }
            }
            rank++;
        }
        for (int i = 0; i < min(width(), height()); ++i) {
            res *= A[i][i];
        }
        return res;
    }
};

struct SemiRing {
    using T = mint;
    static inline T mul(T x, T y){ return x * y; }
    static inline void add(T &x, T y){ x += y; }
    static inline T one(){ return 1; }
    static inline T zero(){ return 0; }
};


template <class T>
ostream& operator<<(ostream& os, vector<T> v) {
    os << "{";
    for (int i = 0; i < v.size(); ++i) {
        if(i) os << ", ";
        os << v[i];
    }
    return os << "}";
}

template <class L, class R>
ostream& operator<<(ostream& os, pair<L, R> p) {
    return os << "{" << p.first << ", " << p.second << "}";
}


int main() {
    int n, m;
    cin >> n >> m;
    vector<tuple<int, int, int>> v;
    vector<int> z(m);
    for (int i = 0; i < m; ++i) {
        int s, t, d;
        scanf("%d %d %d", &s, &t, &d);
        s--; t--;
        v.emplace_back(d, s, t);
        z[i] = d;
    }
    sort(z.begin(), z.end());
    z.erase(unique(z.begin(), z.end()), z.end());
    vector<vector<pair<int, int>>> edges(z.size());
    for (int i = 0; i < m; ++i) {
        int d, s, t; tie(d, s, t) = v[i];
        d = lower_bound(z.begin(),z.end(), d) - z.begin();
        edges[d].emplace_back(s, t);
    }
    QuickFind uf(n);
    int val = 0;
    for (int i = 0; i < z.size(); ++i) {
        for (auto &&e : edges[i]) {
            uf.unite(e.first, e.second);
            if(uf.size() == 1){
                val = i;
                i = z.size();
                break;
            }
        }
    }
    printf("%d ", z[val]);
    uf = QuickFind(n);
    for (int i = 0; i < val; ++i) {
        for (auto &&e : edges[i]) {
            uf.unite(e.first, e.second);
        }
    }
    vector<int> roots;
    for (int i = 0; i < n; ++i) {
        if(!uf.v[i].empty()) roots.emplace_back(i);
    }
    mint ans = 0;
    int C = roots.size();
    for (int i = 1; i < (1 << C)-1; ++i) {
        QuickFind uf2(uf);
        for (int j = 0; j < C; ++j) {
            for (int k = j+1; k < C; ++k) {
                if(((i >> j)& 1) == ((i >> k)& 1)){
                    uf2.unite(roots[j], roots[k]);
                }
            }
        }
        vector<int> l, r;
        for (int j = 0; j < C; ++j) {
            if(i & (1 << j)) {
                if(l.empty()) l = uf2.components(roots[j]);
            }else {
                if(r.empty()) r = uf2.components(roots[j]);
            }
        }
        sort(l.begin(),l.end());
        sort(r.begin(),r.end());
        mint ret = 1;
        if(l.size() != 1){
            int sz = l.size()-1;
            matrix<SemiRing> A(sz, sz);
            for (int j = 0; j < z.size(); ++j) {
                for (auto &&e : edges[j]) {
                    if(uf2.same(e.first, l[0]) && uf2.same(e.second, l[0])){
                        int x = lower_bound(l.begin(),l.end(), e.first) - l.begin();
                        int y = lower_bound(l.begin(),l.end(), e.second) - l.begin();
                        if(x) A[x-1][x-1] += 1;
                        if(y) A[y-1][y-1] += 1;
                        if(x && y) A[x-1][y-1] -= 1, A[y-1][x-1] -= 1;
                    }
                }
            }
            ret *= A.gauss_jordan();
        }
        if(r.size() != 1){
            int sz = r.size()-1;
            matrix<SemiRing> A(sz, sz);
            for (int j = 0; j < z.size(); ++j) {
                for (auto &&e : edges[j]) {
                    if(uf2.same(e.first, r[0]) && uf2.same(e.second, r[0])){
                        int x = lower_bound(r.begin(),r.end(), e.first) - r.begin();
                        int y = lower_bound(r.begin(),r.end(), e.second) - r.begin();
                        if(x) A[x-1][x-1] += 1;
                        if(y) A[y-1][y-1] += 1;
                        if(x && y) A[x-1][y-1] -= 1, A[y-1][x-1] -= 1;
                    }
                }
            }
            ret *= A.gauss_jordan();
        }
        ans += ret;
    }
    printf("%d\n", ans.val);
    return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 3388
final_rank: 7

code_snippet: #include <map>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
template<unsigned mod>
class modint {
private:
    unsigned x;
public:
    modint() : x(0) {};
    modint(long long x_) : x(x_ >= 0 ? x_ % mod : (mod - (-x_) % mod) % mod) {};
    unsigned get() { return x; }
    bool operator==(const modint& m) const { return x == m.x; }
    bool operator!=(const modint& m) const { return x != m.x; }
    modint& operator+=(const modint& m) { x += m.x; x = (x < mod ? x : x - mod); return *this; }
    modint& operator-=(const modint& m) { x += mod - m.x; x = (x < mod ? x : x - mod); return *this; }
    modint& operator*=(const modint& m) { x = 1ULL * x * m.x % mod; return *this; }
    modint operator+(const modint& m) const { return modint(*this) += m; }
    modint operator-(const modint& m) const { return modint(*this) -= m; }
    modint operator*(const modint& m) const { return modint(*this) *= m; }
    modint pow(unsigned long long b) const {
        modint ans(1), mul(*this);
        while(b > 0) {
            if(b & 1) ans *= mul;
            mul *= mul;
            b >>= 1;
        }
        return ans;
    }
    modint inv() const { return pow(mod - 2); }
};
using mint = modint<1000000007>;
mint determinant(int n, vector<vector<mint> > a) {
    mint ans = 1;
    for(int i = 0; i < n; ++i) {
        int ptr = -1;
        for(int j = i; j < n; ++j) {
            if(a[i][j] != mint(0)) {
                ptr = j;
                break;
            }
        }
        if(ptr == -1) return mint(0);
        if(ptr > i) {
            swap(a[i], a[ptr]);
            ans *= mint(-1);
        }
        mint mul = a[i][i], mulinv = mul.inv();
        ans *= mul;
        for(int j = i; j < n; ++j) {
            a[i][j] *= mulinv;
        }
        for(int j = i + 1; j < n; ++j) {
            mint coeff = a[j][i];
            for(int k = 0; k < n; ++k) {
                a[j][k] -= coeff * a[i][k];
            }
        }
    }
    return ans;
}
int main() {
    int N, M;
    cin >> N >> M;
    vector<int> S(M), T(M), D(M);
    for(int i = 0; i < M; ++i) {
        cin >> S[i] >> T[i] >> D[i]; --S[i], --T[i];
    }
    vector<int> sd = D;
    sort(sd.begin(), sd.end());
    sd.erase(unique(sd.begin(), sd.end()), sd.end());
    vector<vector<int> > comps;
    for(int i = 0; i < N; ++i) {
        comps.push_back(vector<int>({ i }));
    }
    int threshold = -1;
    for(int i : sd) {
        vector<vector<int> > sg(N);
        for(int j = 0; j < M; ++j) {
            if(D[j] <= i) {
                sg[S[j]].push_back(T[j]);
                sg[T[j]].push_back(S[j]);
            }
        }
        vector<vector<int> > ncomps;
        vector<bool> vis(N);
        for(int j = 0; j < N; ++j) {
            if(vis[j]) continue;
            queue<int> que;
            que.push(j);
            vis[j] = true;
            vector<int> ncomp;
            while(!que.empty()) {
                int u = que.front(); que.pop();
                ncomp.push_back(u);
                for(int k : sg[u]) {
                    if(!vis[k]) {
                        vis[k] = true;
                        que.push(k);
                    }
                }
            }
            sort(ncomp.begin(), ncomp.end());
            ncomps.push_back(ncomp);
        }
        if(ncomps.size() == 1) {
            threshold = i;
            break;
        }
        comps = ncomps;
    }
    int C = comps.size();
    vector<mint> sub(1 << C);
    for(int i = 1; i < 1 << C; ++i) {
        vector<int> verts;
        for (int j = 0; j < C; ++j) {
            if ((i >> j) & 1) {
                verts.insert(verts.end(), comps[j].begin(), comps[j].end());
            }
        }
        sort(verts.begin(), verts.end());
        int V = verts.size();
        vector<vector<mint> > mat(V, vector<mint>(V));
        for (int j = 0; j < M; ++j) {
            int pa = lower_bound(verts.begin(), verts.end(), S[j]) - verts.begin();
            int pb = lower_bound(verts.begin(), verts.end(), T[j]) - verts.begin();
            if (pa != V && verts[pa] == S[j] && pb != V && verts[pb] == T[j]) {
                mat[pa][pb] = mint(-1);
                mat[pb][pa] = mint(-1);
                mat[pa][pa] += 1;
                mat[pb][pb] += 1;
            }
        }
        vector<vector<mint> > redmat(V - 1, vector<mint>(V - 1));
        for (int j = 0; j < V - 1; ++j) {
            for (int k = 0; k < V - 1; ++k) {
                redmat[j][k] = mat[j][k];
            }
        }
        sub[i] = determinant(V - 1, redmat);
    }
    mint ans = 0;
    for(int i = 1; i < (1 << C) - 1; ++i) {
        ans += sub[i] * sub[(1 << C) - i - 1];
    }
    cout << threshold << ' ' << ans.get() << endl;
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3288
final_rank: 1

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}


struct FastIO{
  FastIO(){
    cin.tie(0);
    ios::sync_with_stdio(0);
  }
}fastio_beet;


template<typename K>
struct Matrix{
  typedef vector<K> arr;
  typedef vector<arr> mat;
  mat dat;

  Matrix(size_t r,size_t c):dat(r,arr(c,K())){}
  Matrix(mat dat):dat(dat){}

  size_t size() const{return dat.size();}
  bool empty() const{return size()==0;}
  arr& operator[](size_t k){return dat[k];}
  const arr& operator[](size_t k) const {return dat[k];}

  static Matrix cross(const Matrix &A,const Matrix &B){
    Matrix res(A.size(),B[0].size());
    for(int i=0;i<(int)A.size();i++)
      for(int j=0;j<(int)B[0].size();j++)
        for(int k=0;k<(int)B.size();k++)
          res[i][j]+=A[i][k]*B[k][j];
    return res;
  }

  static Matrix identity(size_t n){
    Matrix res(n,n);
    for(int i=0;i<(int)n;i++) res[i][i]=K(1);
    return res;
  }

  Matrix pow(long long n) const{
    Matrix a(dat),res=identity(size());
    while(n){
      if(n&1) res=cross(res,a);
      a=cross(a,a);
      n>>=1;
    }
    return res;
  }

  template<typename T>
  using ET = enable_if<is_floating_point<T>::value>;
  template<typename T>
  using EF = enable_if<!is_floating_point<T>::value>;

  template<typename T, typename ET<T>::type* = nullptr>
  static bool is_zero(T x){return abs(x)<1e-8;}
  template<typename T, typename EF<T>::type* = nullptr>
  static bool is_zero(T x){return x==T(0);}

  template<typename T, typename ET<T>::type* = nullptr>
  static bool compare(T x,T y){return abs(x)<abs(y);}
  template<typename T, typename EF<T>::type* = nullptr>
  static bool compare(T x,T y){(void)x;return y!=T(0);}

  // assume regularity
  static Matrix gauss_jordan(const Matrix &A,const Matrix &B){
    int n=A.size(),l=B[0].size();
    Matrix C(n,n+l);
    for(int i=0;i<n;i++){
      for(int j=0;j<n;j++)
        C[i][j]=A[i][j];
      for(int j=0;j<l;j++)
        C[i][n+j]=B[i][j];
    }
    for(int i=0;i<n;i++){
      int p=i;
      for(int j=i;j<n;j++)
        if(compare(C[p][i],C[j][i])) p=j;
      swap(C[i],C[p]);
      if(is_zero(C[i][i])) return Matrix(0,0);
      for(int j=i+1;j<n+l;j++) C[i][j]/=C[i][i];
      for(int j=0;j<n;j++){
        if(i==j) continue;
        for(int k=i+1;k<n+l;k++)
          C[j][k]-=C[j][i]*C[i][k];
      }
    }
    Matrix res(n,l);
    for(int i=0;i<n;i++)
      for(int j=0;j<l;j++)
        res[i][j]=C[i][n+j];
    return res;
  }

  Matrix inv() const{
    Matrix B=identity(size());
    return gauss_jordan(*this,B);
  }

  static arr linear_equations(const Matrix &A,const arr &b){
    Matrix B(b.size(),1);
    for(int i=0;i<(int)b.size();i++) B[i][0]=b[i];
    Matrix tmp=gauss_jordan(A,B);
    arr res(tmp.size());
    for(int i=0;i<(int)tmp.size();i++) res[i]=tmp[i][0];
    return res;
  }

  K determinant() const{
    Matrix A(dat);
    K res(1);
    int n=size();
    for(int i=0;i<n;i++){
      int p=i;
      for(int j=i;j<n;j++)
        if(compare(A[p][i],A[j][i])) p=j;
      if(i!=p) swap(A[i],A[p]),res=-res;
      if(is_zero(A[i][i])) return K(0);
      res*=A[i][i];
      for(int j=i+1;j<n;j++) A[i][j]/=A[i][i];
      for(int j=i+1;j<n;j++)
        for(int k=i+1;k<n;k++)
          A[j][k]-=A[j][i]*A[i][k];
    }
    return res;
  }

  static K sigma(K x,long long n){
    Matrix A(2,2);
    A[0][0]=x;A[0][1]=0;
    A[1][0]=1;A[1][1]=1;
    return A.pow(n)[1][0];
  }
};


struct UnionFind{
  int num;
  vector<int> r,p;
  UnionFind(){}
  UnionFind(int n):num(n),r(n,1),p(n,0){iota(p.begin(),p.end(),0);}
  int find(int x){
    return (x==p[x]?x:p[x]=find(p[x]));
  }
  bool same(int x,int y){
    return find(x)==find(y);
  }
  void unite(int x,int y){
    x=find(x);y=find(y);
    if(x==y) return;
    if(r[x]<r[y]) swap(x,y);
    r[x]+=r[y];
    p[y]=x;
    num--;
  }
  int size(int x){
    return r[find(x)];
  }
  int count() const{
    return num;
  }
};


template<typename K, typename T>
struct MatrixTreeTheorem{
  using M = Matrix<K>;
  struct edge{
    int a,b;
    T c;
    edge(int a,int b,T c):a(a),b(b),c(c){}
    bool operator<(const edge &e)const{
      return c<e.c;
    }
  };

  int n;
  vector<edge> es;
  vector<int> used;
  vector<vector<int> > H;

  MatrixTreeTheorem(){}
  MatrixTreeTheorem(int n):n(n),used(n),H(n){}

  void add_edge(int a,int b,T c){
    es.emplace_back(a,b,c);
  }

  vector<int> bfs(int v){
    vector<int> vs;
    queue<int> q;
    used[v]=1;
    q.emplace(v);
    while(!q.empty()){
      v=q.front();q.pop();
      vs.emplace_back(v);
      for(int u:H[v]){
        if(used[u]) continue;
        used[u]=1;
        q.emplace(u);
      }
    }
    return vs;
  }

  K build(){
    UnionFind uf(n);
    sort(es.begin(),es.end());
    fill(used.begin(),used.end(),0);
    K res(1);
    for(int i=0;i<(int)es.size();){
      queue<int> q,r;
      for(auto &h:H) h.clear();
      int p=i;
      while(i<(int)es.size()&&es[i].c==es[p].c){
        int u=uf.find(es[i].a);
        int v=uf.find(es[i++].b);
        H[u].emplace_back(v);
        H[v].emplace_back(u);
        q.emplace(u);q.emplace(v);
        r.emplace(u);r.emplace(v);
      }
      while(!q.empty()){
        int v=q.front();q.pop();
        if(used[v]) continue;
        vector<int> vs=bfs(v);
        int m=vs.size();
        if(m==1) continue;

        sort(vs.begin(),vs.end());
        auto idx=
          [&](int x){
            return lower_bound(vs.begin(),vs.end(),x)-vs.begin();
          };
        M A(m,m);
        for(int x:vs)
          for(int y:H[x])
            if(x!=y) A[idx(x)][idx(y)]-=K(1);

        for(int x=0;x<m;x++)
          for(int y=0;y<m;y++)
            if(x!=y) A[x][x]-=A[x][y];

        A.dat.pop_back();
        for(auto &a:A.dat) a.pop_back();

        res*=A.determinant();
        for(int x:vs) uf.unite(v,x);
      }
      while(!r.empty()){
        int v=r.front();r.pop();
        used[v]=0;
      }
    }
    return res;
  }
};


template<typename T,T MOD = 1000000007>
struct Mint{
  static constexpr T mod = MOD;
  T v;
  Mint():v(0){}
  Mint(signed v):v(v){}
  Mint(long long t){v=t%MOD;if(v<0) v+=MOD;}

  Mint pow(long long k){
    Mint res(1),tmp(v);
    while(k){
      if(k&1) res*=tmp;
      tmp*=tmp;
      k>>=1;
    }
    return res;
  }

  static Mint add_identity(){return Mint(0);}
  static Mint mul_identity(){return Mint(1);}

  Mint inv(){return pow(MOD-2);}

  Mint& operator+=(Mint a){v+=a.v;if(v>=MOD)v-=MOD;return *this;}
  Mint& operator-=(Mint a){v+=MOD-a.v;if(v>=MOD)v-=MOD;return *this;}
  Mint& operator*=(Mint a){v=1LL*v*a.v%MOD;return *this;}
  Mint& operator/=(Mint a){return (*this)*=a.inv();}

  Mint operator+(Mint a) const{return Mint(v)+=a;}
  Mint operator-(Mint a) const{return Mint(v)-=a;}
  Mint operator*(Mint a) const{return Mint(v)*=a;}
  Mint operator/(Mint a) const{return Mint(v)/=a;}

  Mint operator-() const{return v?Mint(MOD-v):Mint(v);}

  bool operator==(const Mint a)const{return v==a.v;}
  bool operator!=(const Mint a)const{return v!=a.v;}
  bool operator <(const Mint a)const{return v <a.v;}

  static Mint comb(long long n,int k){
    Mint num(1),dom(1);
    for(int i=0;i<k;i++){
      num*=Mint(n-i);
      dom*=Mint(i+1);
    }
    return num/dom;
  }
};
template<typename T,T MOD> constexpr T Mint<T, MOD>::mod;
template<typename T,T MOD>
ostream& operator<<(ostream &os,Mint<T, MOD> m){os<<m.v;return os;}

//INSERT ABOVE HERE
signed main(){
  int n,m;
  cin>>n>>m;
  using M = Mint<int>;
  using edge=MatrixTreeTheorem<M, int>::edge;
  vector<edge> es;
  for(int i=0;i<m;i++){
    int s,t,d;
    cin>>s>>t>>d;
    s--;t--;
    es.emplace_back(s,t,d);
  }
  sort(es.begin(),es.end());
  UnionFind uf(n);
  int cost=0;
  for(auto e:es){
    uf.unite(e.a,e.b);
    cost=e.c;
    if(uf.count()==1) break;
  }
  cout<<cost<<" ";

  uf=UnionFind(n);
  for(auto e:es){
    if(e.c==cost) break;
    uf.unite(e.a,e.b);
  }

  vector<int> rs;
  for(int i=0;i<n;i++)
    if(uf.find(i)==i) rs.emplace_back(i);

  M ans{0};
  int k=rs.size();
  int sz=1<<k;
  for(int b=1;b+1<sz;b++){
    UnionFind tmp(uf);
    for(int i=0;i<k;i++)
      for(int j=0;j<k;j++)
        if(((b>>i)&1)==((b>>j)&1))
          tmp.unite(rs[i],rs[j]);

    UnionFind ch(n);
    for(auto e:es)
      if(tmp.same(e.a,e.b))
        ch.unite(e.a,e.b);
    if(ch.count()!=2) continue;

    MatrixTreeTheorem<M, int> G(n);
    for(auto e:es)
      if(tmp.same(e.a,e.b))
        G.add_edge(e.a,e.b,0);

    ans+=G.build();
  }
  cout<<ans<<endl;
  return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 3304
final_rank: 2

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}


struct FastIO{
  FastIO(){
    cin.tie(0);
    ios::sync_with_stdio(0);
  }
}fastio_beet;


template<typename K>
struct Matrix{
  typedef vector<K> arr;
  typedef vector<arr> mat;
  mat dat;

  Matrix(size_t r,size_t c):dat(r,arr(c,K())){}
  Matrix(mat dat):dat(dat){}

  size_t size() const{return dat.size();}
  bool empty() const{return size()==0;}
  arr& operator[](size_t k){return dat[k];}
  const arr& operator[](size_t k) const {return dat[k];}

  static Matrix cross(const Matrix &A,const Matrix &B){
    Matrix res(A.size(),B[0].size());
    for(int i=0;i<(int)A.size();i++)
      for(int j=0;j<(int)B[0].size();j++)
        for(int k=0;k<(int)B.size();k++)
          res[i][j]+=A[i][k]*B[k][j];
    return res;
  }

  static Matrix identity(size_t n){
    Matrix res(n,n);
    for(int i=0;i<(int)n;i++) res[i][i]=K(1);
    return res;
  }

  Matrix pow(long long n) const{
    Matrix a(dat),res=identity(size());
    while(n){
      if(n&1) res=cross(res,a);
      a=cross(a,a);
      n>>=1;
    }
    return res;
  }

  template<typename T>
  using ET = enable_if<is_floating_point<T>::value>;
  template<typename T>
  using EF = enable_if<!is_floating_point<T>::value>;

  template<typename T, typename ET<T>::type* = nullptr>
  static bool is_zero(T x){return abs(x)<1e-8;}
  template<typename T, typename EF<T>::type* = nullptr>
  static bool is_zero(T x){return x==T(0);}

  template<typename T, typename ET<T>::type* = nullptr>
  static bool compare(T x,T y){return abs(x)<abs(y);}
  template<typename T, typename EF<T>::type* = nullptr>
  static bool compare(T x,T y){(void)x;return y!=T(0);}

  // assume regularity
  static Matrix gauss_jordan(const Matrix &A,const Matrix &B){
    int n=A.size(),l=B[0].size();
    Matrix C(n,n+l);
    for(int i=0;i<n;i++){
      for(int j=0;j<n;j++)
        C[i][j]=A[i][j];
      for(int j=0;j<l;j++)
        C[i][n+j]=B[i][j];
    }
    for(int i=0;i<n;i++){
      int p=i;
      for(int j=i;j<n;j++)
        if(compare(C[p][i],C[j][i])) p=j;
      swap(C[i],C[p]);
      if(is_zero(C[i][i])) return Matrix(0,0);
      for(int j=i+1;j<n+l;j++) C[i][j]/=C[i][i];
      for(int j=0;j<n;j++){
        if(i==j) continue;
        for(int k=i+1;k<n+l;k++)
          C[j][k]-=C[j][i]*C[i][k];
      }
    }
    Matrix res(n,l);
    for(int i=0;i<n;i++)
      for(int j=0;j<l;j++)
        res[i][j]=C[i][n+j];
    return res;
  }

  Matrix inv() const{
    Matrix B=identity(size());
    return gauss_jordan(*this,B);
  }

  static arr linear_equations(const Matrix &A,const arr &b){
    Matrix B(b.size(),1);
    for(int i=0;i<(int)b.size();i++) B[i][0]=b[i];
    Matrix tmp=gauss_jordan(A,B);
    arr res(tmp.size());
    for(int i=0;i<(int)tmp.size();i++) res[i]=tmp[i][0];
    return res;
  }

  K determinant() const{
    Matrix A(dat);
    K res(1);
    int n=size();
    for(int i=0;i<n;i++){
      int p=i;
      for(int j=i;j<n;j++)
        if(compare(A[p][i],A[j][i])) p=j;
      if(i!=p) swap(A[i],A[p]),res=-res;
      if(is_zero(A[i][i])) return K(0);
      res*=A[i][i];
      for(int j=i+1;j<n;j++) A[i][j]/=A[i][i];
      for(int j=i+1;j<n;j++)
        for(int k=i+1;k<n;k++)
          A[j][k]-=A[j][i]*A[i][k];
    }
    return res;
  }

  static K sigma(K x,long long n){
    Matrix A(2,2);
    A[0][0]=x;A[0][1]=0;
    A[1][0]=1;A[1][1]=1;
    return A.pow(n)[1][0];
  }
};


struct UnionFind{
  int num;
  vector<int> r,p;
  UnionFind(){}
  UnionFind(int n):num(n),r(n,1),p(n,0){iota(p.begin(),p.end(),0);}
  int find(int x){
    return (x==p[x]?x:p[x]=find(p[x]));
  }
  bool same(int x,int y){
    return find(x)==find(y);
  }
  void unite(int x,int y){
    x=find(x);y=find(y);
    if(x==y) return;
    if(r[x]<r[y]) swap(x,y);
    r[x]+=r[y];
    p[y]=x;
    num--;
  }
  int size(int x){
    return r[find(x)];
  }
  int count() const{
    return num;
  }
};


template<typename K, typename T>
struct MatrixTreeTheorem{
  using M = Matrix<K>;
  struct edge{
    int a,b;
    T c;
    edge(int a,int b,T c):a(a),b(b),c(c){}
    bool operator<(const edge &e)const{
      return c<e.c;
    }
  };

  int n;
  vector<edge> es;
  vector<int> used;
  vector<vector<int> > H;

  MatrixTreeTheorem(){}
  MatrixTreeTheorem(int n):n(n),used(n),H(n){}

  void add_edge(int a,int b,T c){
    es.emplace_back(a,b,c);
  }

  vector<int> bfs(int v){
    vector<int> vs;
    queue<int> q;
    used[v]=1;
    q.emplace(v);
    while(!q.empty()){
      v=q.front();q.pop();
      vs.emplace_back(v);
      for(int u:H[v]){
        if(used[u]) continue;
        used[u]=1;
        q.emplace(u);
      }
    }
    return vs;
  }

  K build(){
    UnionFind uf(n);
    sort(es.begin(),es.end());
    fill(used.begin(),used.end(),0);
    K res(1);
    for(int i=0;i<(int)es.size();){
      queue<int> q,r;
      for(auto &h:H) h.clear();
      int p=i;
      while(i<(int)es.size()&&es[i].c==es[p].c){
        int u=uf.find(es[i].a);
        int v=uf.find(es[i++].b);
        H[u].emplace_back(v);
        H[v].emplace_back(u);
        q.emplace(u);q.emplace(v);
        r.emplace(u);r.emplace(v);
      }
      while(!q.empty()){
        int v=q.front();q.pop();
        if(used[v]) continue;
        vector<int> vs=bfs(v);
        int m=vs.size();
        if(m==1) continue;

        sort(vs.begin(),vs.end());
        auto idx=
          [&](int x){
            return lower_bound(vs.begin(),vs.end(),x)-vs.begin();
          };
        M A(m,m);
        for(int x:vs)
          for(int y:H[x])
            if(x!=y) A[idx(x)][idx(y)]-=K(1);

        for(int x=0;x<m;x++)
          for(int y=0;y<m;y++)
            if(x!=y) A[x][x]-=A[x][y];

        A.dat.pop_back();
        for(auto &a:A.dat) a.pop_back();

        res*=A.determinant();
        for(int x:vs) uf.unite(v,x);
      }
      while(!r.empty()){
        int v=r.front();r.pop();
        used[v]=0;
      }
    }
    return res;
  }
};


template<typename T,T MOD = 1000000007>
struct Mint{
  static constexpr T mod = MOD;
  T v;
  Mint():v(0){}
  Mint(signed v):v(v){}
  Mint(long long t){v=t%MOD;if(v<0) v+=MOD;}

  Mint pow(long long k){
    Mint res(1),tmp(v);
    while(k){
      if(k&1) res*=tmp;
      tmp*=tmp;
      k>>=1;
    }
    return res;
  }

  static Mint add_identity(){return Mint(0);}
  static Mint mul_identity(){return Mint(1);}

  Mint inv(){return pow(MOD-2);}

  Mint& operator+=(Mint a){v+=a.v;if(v>=MOD)v-=MOD;return *this;}
  Mint& operator-=(Mint a){v+=MOD-a.v;if(v>=MOD)v-=MOD;return *this;}
  Mint& operator*=(Mint a){v=1LL*v*a.v%MOD;return *this;}
  Mint& operator/=(Mint a){return (*this)*=a.inv();}

  Mint operator+(Mint a) const{return Mint(v)+=a;}
  Mint operator-(Mint a) const{return Mint(v)-=a;}
  Mint operator*(Mint a) const{return Mint(v)*=a;}
  Mint operator/(Mint a) const{return Mint(v)/=a;}

  Mint operator-() const{return v?Mint(MOD-v):Mint(v);}

  bool operator==(const Mint a)const{return v==a.v;}
  bool operator!=(const Mint a)const{return v!=a.v;}
  bool operator <(const Mint a)const{return v <a.v;}

  static Mint comb(long long n,int k){
    Mint num(1),dom(1);
    for(int i=0;i<k;i++){
      num*=Mint(n-i);
      dom*=Mint(i+1);
    }
    return num/dom;
  }
};
template<typename T,T MOD> constexpr T Mint<T, MOD>::mod;
template<typename T,T MOD>
ostream& operator<<(ostream &os,Mint<T, MOD> m){os<<m.v;return os;}

//INSERT ABOVE HERE
signed main(){
  int n,m;
  cin>>n>>m;
  using M = Mint<int>;
  using edge=MatrixTreeTheorem<M, int>::edge;
  vector<edge> es;
  for(int i=0;i<m;i++){
    int s,t,d;
    cin>>s>>t>>d;
    s--;t--;
    es.emplace_back(s,t,d);
  }
  sort(es.begin(),es.end());
  UnionFind uf(n);
  int cost=0;
  for(auto e:es){
    uf.unite(e.a,e.b);
    cost=e.c;
    if(uf.count()==1) break;
  }
  cout<<cost<<" ";

  uf=UnionFind(n);
  for(auto e:es){
    if(e.c==cost) break;
    uf.unite(e.a,e.b);
  }

  vector<int> rs;
  for(int i=0;i<n;i++)
    if(uf.find(i)==i) rs.emplace_back(i);

  M ans{0};
  int k=rs.size();
  int sz=1<<k;
  for(int b=1;b+1<sz;b++){
    UnionFind tmp(uf);
    for(int i=0;i<k;i++)
      for(int j=0;j<k;j++)
        if(((b>>i)&1)==((b>>j)&1))
          tmp.unite(rs[i],rs[j]);

    if(tmp.count()!=2) continue;

    MatrixTreeTheorem<M, int> G(n);
    for(auto e:es)
      if(tmp.same(e.a,e.b))
        G.add_edge(e.a,e.b,0);

    ans+=G.build();
  }
  cout<<ans<<endl;
  return 0;
}
accuracy: 0.13333333333333333
time_ms: 50
memory_kb: 3260
final_rank: 16

code_snippet: #include <stdio.h>
#include <set>
#include <queue>
#include <map>
#include <utility>
using namespace std;
const int MOD=1e9+7;
const int N=1e2+10;
int p[N];
int find(int n){
    return p[n]==n?n:p[n]=find(p[n]);
}
struct side{
    int from;
    int to;
    int length;
    void in(){
        scanf("%d%d%d",&from,&to,&length);
        return ;
    }
    void pull(){
        from=find(from);
        to=find(to);
        return ;
    }
    bool out(){
        return from==to;
    }
    void operator =(side a){
        from=a.from;
        to=a.to;
        length=a.length;
        return ;
    }
};
struct cmp{
    bool operator ()(const side& a,const side& b){
        return a.length>b.length;
    }
};
struct matrix{
    int size;
    vector<vector<int>> v;
    void init(int n){
        v.clear();
        size=n;
        vector<int> emp;
        emp.clear();
        for(int i=0;i<n;i++){
            v.push_back(emp);
            for(int j=0;j<n;j++)v.back().push_back(0);
        }
        return ;
    }
};
set<int> graph[N],ng[N],ins;
int n,size[N],ns=0;
long long int ans=0;
void swap(int &a,int &b){
    int temp=a;
    a=b;
    b=temp;
    return ;
}
long long int gcd(long long int a,long long int b){
    if(b==0)return a;
    return gcd(b,a%b)%MOD;
}
long long int pow(long long int a,int t){
    if(t==0)return 1;
    long long int temp=pow(a,t/2);
    temp*=temp;
    temp%=MOD;
    if(t&1){
        temp*=a;
        temp%=MOD;
    }
    return temp;
}
long long int rev(long long int a){
    return pow(a,MOD-2);
}
long long int det(matrix &m){
    long long int temp=1,piv,mul;
    for(int i=0;i<m.size;i++){
        if(m.v[i][i]==0){
            for(int j=i+1;j<m.size;j++)if(m.v[j][i]!=0){
                for(int k=i;k<m.size;k++)swap(m.v[j][k],m.v[i][k]);
                break;
            }
        }
        for(int j=i+1;j<m.size;j++)if(m.v[j][i]!=0){
            piv=m.v[i][i]/gcd(m.v[i][i],m.v[j][i]);
            mul=m.v[j][i]/gcd(m.v[i][i],m.v[j][i]);
            for(int k=i;k<m.size;k++)m.v[j][k]=(m.v[j][k]*piv-m.v[i][k]*mul)%MOD;
            temp*=rev(piv);
            temp%=MOD;
        }
    }
    for(int i=0;i<m.size;i++){
        temp*=m.v[i][i];
        temp%=MOD;
    }
    return temp;
}
long long int lap(set<int>& s){
    matrix m;
    map<int,int> ma;
    int now=0;
    for(int i=1;i<=n;i++)if(s.find(find(i))!=s.end())ma.insert({i,now++});
    if(ma.size()==1)return 1;
    m.init((int)ma.size());
    for(pair<int,int> i:ma){
        for(int j:graph[i.first]){
            if(ma.find(j)!=ma.end()){
                m.v[i.second][i.second]++;
                m.v[i.second][ma[j]]=-1;
            }
        }
    }
    for(int i=0;i<m.size;i++)m.v[i][0]=m.v[0][i]=0;
    m.v[0][0]=1;
    return det(m);
}
void dfs(int now){
    if(now>n){
        if(!ins.empty()){
            queue<int> q;
            set<int> in=ins,left,right;
            int temp;
            q.push(*in.begin());
            in.erase(in.begin());
            while(!q.empty()){
                temp=q.front();
                q.pop();
                for(int i:ng[temp]){
                    if(in.find(i)!=in.end()){
                        in.erase(i);
                        q.push(i);
                    }
                }
            }
            if(in.empty()){
                for(int i=1;i<=n;i++)if(p[i]==i&&ins.find(i)==ins.end())in.insert(i);
                if(!in.empty()){
                    q.push(*in.begin());
                    in.erase(in.begin());
                    while(!q.empty()){
                        temp=q.front();
                        q.pop();
                        for(int i:ng[temp]){
                            if(in.find(i)!=in.end()){
                                in.erase(i);
                                q.push(i);
                            }
                        }
                    }
                    if(in.empty()){
                        for(int i=1;i<=n;i++)if(p[i]==i){
                            if(ins.find(i)==ins.end())left.insert(i);
                            else right.insert(i);
                        }
                        ans+=(lap(left)*lap(right))%MOD;
                        ans%=MOD;
                    }
                }
            }
        }
        return ;
    }
    if(p[now]==now){
        ins.insert(now);
        ns+=size[now];
        dfs(now+1);
        ins.erase(now);
        ns-=size[now];
    }
    dfs(now+1);
    return ;
}
int main(){
    int m,now,in,l,r;
    side temp,s[N*N];
    priority_queue<side,vector<side>,cmp> pq;
    scanf("%d%d",&n,&m);
    in=n;
    for(int i=1;i<=n;i++)p[i]=i;
    for(int i=0;i<m;i++){
        temp.in();
        s[i]=temp;
        graph[temp.from].insert(temp.to);
        graph[temp.to].insert(temp.from);
        pq.push(temp);
    
    }
    now=pq.top().length;
    while(in>1){
        temp=pq.top();
        now=temp.length;
        pq.pop();
        temp.pull();
        if(!temp.out()){
            p[temp.from]=temp.to;
            in--;
        }
        while(!pq.empty()){
            if(pq.top().length<=now){
                temp=pq.top();
                pq.pop();
                temp.pull();
                if(!temp.out()){
                    p[temp.from]=temp.to;
                    in--;
                }
            }
            else break;
        }
    }
    for(int i=1;i<=n;i++){
        p[i]=i;
        size[i]=0;
    }
    for(int i=0;i<m;i++)if(s[i].length<now){
        s[i].pull();
        p[s[i].from]=s[i].to;
    }
    for(int i=1;i<=n;i++){
        l=find(i);
        size[l]++;
        for(int j:graph[i]){
            r=find(j);
            if(l!=r){
                ng[l].insert(r);
                ng[r].insert(l);
            }
        }
    }
    dfs(1);
    if(ans<0){
        ans%=MOD;
        ans+=MOD;
    }
    printf("%d %lld\n",now,ans%MOD);
}
accuracy: 1
time_ms: 20
memory_kb: 3440
final_rank: 3

code_snippet: #include <stdio.h>
#include <set>
#include <queue>
#include <map>
#include <utility>
using namespace std;
const int MOD=1e9+7;
const int N=1e2+10;
int p[N];
int find(int n){
    return p[n]==n?n:p[n]=find(p[n]);
}
struct side{
    int from;
    int to;
    int length;
    void in(){
        scanf("%d%d%d",&from,&to,&length);
        return ;
    }
    void pull(){
        from=find(from);
        to=find(to);
        return ;
    }
    bool out(){
        return from==to;
    }
    void operator =(side a){
        from=a.from;
        to=a.to;
        length=a.length;
        return ;
    }
};
struct cmp{
    bool operator ()(const side& a,const side& b){
        return a.length>b.length;
    }
};
struct matrix{
    int size;
    vector<vector<int>> v;
    void init(int n){
        v.clear();
        size=n;
        vector<int> emp;
        emp.clear();
        for(int i=0;i<n;i++){
            v.push_back(emp);
            for(int j=0;j<n;j++)v.back().push_back(0);
        }
        return ;
    }
};
set<int> graph[N],ng[N],ins;
int n,size[N],ns=0;
long long int ans=0;
void swap(int &a,int &b){
    int temp=a;
    a=b;
    b=temp;
    return ;
}
long long int gcd(long long int a,long long int b){
    if(b==0)return a;
    return gcd(b,a%b)%MOD;
}
long long int pow(long long int a,int t){
    if(t==0)return 1;
    long long int temp=pow(a,t/2);
    temp*=temp;
    temp%=MOD;
    if(t&1){
        temp*=a;
        temp%=MOD;
    }
    return temp;
}
long long int rev(long long int a){
    return pow(a,MOD-2);
}
long long int det(matrix &m){
    long long int temp=1,piv,mul;
    int a=1;
    for(int i=0;i<m.size;i++){
        if(m.v[i][i]==0){
            for(int j=i+1;j<m.size;j++)if(m.v[j][i]!=0){
                for(int k=i;k<m.size;k++)swap(m.v[j][k],m.v[i][k]);
                break;
            }
        }
        if(m.v[i][i]<0){
            a*=-1;
            for(int j=i;j<m.size;j++)m.v[i][j]*=-1;
        }
        for(int j=i+1;j<m.size;j++)if(m.v[j][i]!=0){
            if(m.v[j][i]<0){
                a*=-1;
                for(int k=i;k<m.size;k++)m.v[j][k]*=-1;
            }
            piv=m.v[i][i]/gcd(m.v[i][i],m.v[j][i]);
            mul=m.v[j][i]/gcd(m.v[i][i],m.v[j][i]);
            for(int k=i;k<m.size;k++)m.v[j][k]=(m.v[j][k]*piv-m.v[i][k]*mul)%MOD;
            temp*=rev(piv);
            temp%=MOD;
        }
    }
    for(int i=0;i<m.size;i++){
        temp*=m.v[i][i];
        temp%=MOD;
    }
    return temp*a;
}
long long int lap(set<int>& s){
    matrix m;
    map<int,int> ma;
    int now=0;
    for(int i=1;i<=n;i++)if(s.find(find(i))!=s.end())ma.insert({i,now++});
    if(ma.size()==1)return 1;
    m.init((int)ma.size());
    for(pair<int,int> i:ma){
        for(int j:graph[i.first]){
            if(ma.find(j)!=ma.end()){
                m.v[i.second][i.second]++;
                m.v[i.second][ma[j]]=-1;
            }
        }
    }
    for(int i=0;i<m.size;i++)m.v[i][0]=m.v[0][i]=0;
    m.v[0][0]=1;
    return det(m);
}
void dfs(int now){
    if(now>n){
        if(!ins.empty()){
            queue<int> q;
            set<int> in=ins,left,right;
            int temp;
            q.push(*in.begin());
            in.erase(in.begin());
            while(!q.empty()){
                temp=q.front();
                q.pop();
                for(int i:ng[temp]){
                    if(in.find(i)!=in.end()){
                        in.erase(i);
                        q.push(i);
                    }
                }
            }
            if(in.empty()){
                for(int i=1;i<=n;i++)if(p[i]==i&&ins.find(i)==ins.end())in.insert(i);
                if(!in.empty()){
                    q.push(*in.begin());
                    in.erase(in.begin());
                    while(!q.empty()){
                        temp=q.front();
                        q.pop();
                        for(int i:ng[temp]){
                            if(in.find(i)!=in.end()){
                                in.erase(i);
                                q.push(i);
                            }
                        }
                    }
                    if(in.empty()){
                        for(int i=1;i<=n;i++)if(p[i]==i){
                            if(ins.find(i)==ins.end())left.insert(i);
                            else right.insert(i);
                        }
                        ans+=(lap(left)*lap(right))%MOD;
                        ans%=MOD;
                    }
                }
            }
        }
        return ;
    }
    if(p[now]==now){
        ins.insert(now);
        ns+=size[now];
        dfs(now+1);
        ins.erase(now);
        ns-=size[now];
    }
    dfs(now+1);
    return ;
}
int main(){
    int m,now,in,l,r;
    side temp,s[N*N];
    priority_queue<side,vector<side>,cmp> pq;
    scanf("%d%d",&n,&m);
    in=n;
    for(int i=1;i<=n;i++)p[i]=i;
    for(int i=0;i<m;i++){
        temp.in();
        s[i]=temp;
        graph[temp.from].insert(temp.to);
        graph[temp.to].insert(temp.from);
        pq.push(temp);
    }
    now=pq.top().length;
    while(in>1){
        temp=pq.top();
        now=temp.length;
        pq.pop();
        temp.pull();
        if(!temp.out()){
            p[temp.from]=temp.to;
            in--;
        }
        while(!pq.empty()){
            if(pq.top().length<=now){
                temp=pq.top();
                pq.pop();
                temp.pull();
                if(!temp.out()){
                    p[temp.from]=temp.to;
                    in--;
                }
            }
            else break;
        }
    }
    for(int i=1;i<=n;i++){
        p[i]=i;
        size[i]=0;
    }
    for(int i=0;i<m;i++)if(s[i].length<now){
        s[i].pull();
        p[s[i].from]=s[i].to;
    }
    for(int i=1;i<=n;i++){
        l=find(i);
        size[l]++;
        for(int j:graph[i]){
            r=find(j);
            if(l!=r){
                ng[l].insert(r);
                ng[r].insert(l);
            }
        }
    }
    dfs(1);
    
    printf("%d %lld\n",now,ans);
}
accuracy: 0.06666666666666667
time_ms: 10
memory_kb: 3380
final_rank: 17

code_snippet: #include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
const int N = 100010;
const int INF = 1LL<<55;
const int mod = (1e9)+7;
const double EPS = 1e-8;
const double PI = 6.0 * asin(0.5);
typedef pair<int,int> P;
typedef long long ll;
ostream& operator<<(ostream& o,P p){return o<<"("<<p.first<<","<<p.second<<")";}
istream& operator>>(istream& i,P &p){return i>>p.first>>p.second;}
template<class T> istream& operator>>(istream& i,vector<T> &a){for(auto &t:a)cin>>t;return i;}
template<class T> T Max(T &a,T b){return a=max(a,b);}
template<class T> T Min(T &a,T b){return a=min(a,b);}
template<class T> void prArr(T a,string s=" "){int i=0;for(auto t:a)cout<<(i++?s:"")<<t;cout<<endl;}

class UF{
public:
  int V;
  vector<int> par,rnk,sz;
  UF(){}
  UF(int V):V(V),par(V),rnk(V,0),sz(V,1){for(int i=0;i<V;i++)par[i]=i;}
  
  int find(int x){
    assert(x < V);
    if(par[x]==x)return x;
    return par[x]=find(par[x]);
  }
  
  void unite(int x,int y){
    x=find(x), y=find(y);
    if(x==y)return;
    if(rnk[x]<rnk[y])par[x]=y, sz[y] += sz[x];
    else{
      par[y]=x;
      sz[x] += sz[y];
      if(rnk[x]==rnk[y])rnk[x]++;
    }
  }

  bool same(int x,int y){return find(x)==find(y);}

  int size(int x){return sz[find(x)];}
};

int mod_determinant(vector<vector<int> > A,const int mod = 1e9+7/*素数のみ使用可能*/){
  /*long longにしないと死ぬので注意*/
  auto mod_pow =[&](int x,int n){
    int res=1;
    while(n){
      if(n%2) res=res*x%mod;
      x=x*x%mod;
      n/=2;
    }
    return res;
  };
  
  int n = A.size();
  for(int i=0;i<n;i++)
    for(int j=0;j<n;j++) A[i][j] = (A[i][j]%mod + mod) % mod;
  
  
  int det = 1;
  for(int i=0;i<n;i++){
    int pivot=-1;//次の基準となる添え字。
    for(int j=i;j<n && pivot==-1;j++) if(A[j][i]!=0) pivot=j;//0以外の1つを探す
    
    if(pivot == -1) return 0; // det = 0;
    swap(A[i],A[pivot]);//i行目に持ってくる。
    if(i != pivot) det = (-det + mod) % mod;
    det = det * A[i][i] % mod;
    
    int div = mod_pow(A[i][i],mod-2);
    for(int j=i+1;j<n;j++) A[i][j]=A[i][j]*div%mod; //左辺をA[i][i]で両辺を割る。
    
    for(int j=i+1;j<n;j++){ 
      for(int k=i+1;k<n;k++) A[j][k]=(A[j][k]-A[j][i]*A[i][k]%mod+mod)%mod;
      A[j][i] = 0;
    }
  }
  return det;
}

int calcL(vector<vector<int> > L){
  int n = L.size();
  vector<vector<int> > LL(n-1,vector<int>(n-1));

  for(int i=1;i<n;i++)
    for(int j=1;j<n;j++) LL[i-1][j-1] = L[i][j];
  return mod_determinant(LL);
}

vector<vector<int> > G;
int calc(UF &uf,int par){
  vector<int> num;
  int n = G.size();
  for(int i=0;i<n;i++) if(uf.find(i) == par) num.push_back(i);
  sort(num.begin(),num.end());
    
  int l = num.size();
  vector<vector<int> > L(l,vector<int>(l));
  for(int i:num)
    for(int to:G[i]){
      int a = lower_bound(num.begin(),num.end(),i) - num.begin();
      int b = lower_bound(num.begin(),num.end(),to) - num.begin();
      if(b == l || num[b] != to) continue;
      L[a][b]--;
      L[a][a]++;
    }      
  
  return calcL(L);
};


signed main(){
  int n,m;
  cin>>n>>m;
  typedef pair<int,P> PP;
  vector<PP> edge(m);
  G.resize(n);
  for(int i=0;i<m;i++){
    int s,t,d;
    cin>>s>>t>>d; s--,t--;
    edge[i] = PP(d,P(s,t));
    G[s].push_back(t);
    G[t].push_back(s);
  }
  sort(edge.begin(),edge.end());

  UF uf(n);
  int dis = 0;
  for(int i=0;i<m;i++){
    int idx = i;
    UF tmp = uf;
    dis = edge[idx].first;
    
    while(i<m && edge[idx].first == edge[i].first){
      int a,b; tie(a,b) = edge[i].second;
      tmp.unite(a,b);
      i++;
    }
    i--;
    if(tmp.size(0) == n) break;
    uf = tmp;
  }

  
  vector<int> par;
  for(int i=0;i<n;i++) if(uf.find(i) == i) par.push_back(i);

  int ans = 0;
  int p = par.size();
  for(int bit=0;bit<(1<<p);bit++){
    UF tmp = uf;
    
    for(int i=0;i<p;i++)
      for(int j=0;j<p;j++)
        if((bit>>i&1) == (bit>>j&1)) tmp.unite(par[i],par[j]);
    
    
    int cnt = 0;
    for(int i=0;i<n;i++) if(tmp.find(i) == i) cnt++;
    if(cnt != 2) continue;
    int num = 1;
    for(int i=0;i<n;i++) if(tmp.find(i) == i) num = num * calc(tmp,i)%mod;
    ans = (ans+num)%mod;
  }
  cout<<dis<<" "<<ans<<endl;
  return 0;
}
accuracy: 1
time_ms: 230
memory_kb: 3540
final_rank: 11

code_snippet: #include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<string>
#include<cmath>
#include<cassert>

using namespace std;

#define REP(i, b, e) for( int i = b; i < e; i++ )
#define rep(i, n) REP(i, 0, n)

#define MAX 200
#define INFTY (1<<30)

typedef vector<vector<int> > Matrix;
typedef long long ll;

#define MOD 1000000007

class DisjointSet{
    public:
    DisjointSet(){}
    DisjointSet( int size ){
        rank.resize( size, 0 );
        p.resize( size, 0 );
    }
    
    void makeSet( int x ){
        p[x] = x;
        rank[x] = 0;
    }
    
    void merge( int x, int y ){
        link( findSet(x), findSet(y) );
    }
    
    int findSet( int x ){
        if ( x != p[x] ) p[x] = findSet( p[x] );
        return p[x];
    }
    
    bool isSameSet( int x, int y ){
        return ( findSet(x) == findSet(y) );
    }
    
    private:
    vector<int> rank, p;
    
    void link ( int x, int y ){
        if ( rank[x] > rank[y] ){
            p[y] = x;
        } else {
            p[x] = y;
            if ( rank[x] == rank[y] ) rank[y]++;
        }
    }
};

class Edge{
    public:
    int source, target, cost;
    Edge( int source=0, int target=0, int cost=0 ):
    source(source), target(target), cost(cost){}
    bool operator < ( const Edge &e ) const{
        return cost < e.cost;
    }
};

int kruskal(int N, vector<Edge> edges){ // vector<Edge> &mst
  int lastCost = -1;
  sort( edges.begin(), edges.end() );
  
  DisjointSet dset = DisjointSet( N + 1);
  
  for ( int i = 0; i < N; i++ ) dset.makeSet( i );
  
  int source, target;
  for ( int i = 0; i < edges.size(); i++ ){
    Edge e = edges[i];
    if ( dset.findSet( e.source ) != dset.findSet( e.target ) ){
      lastCost = e.cost;
      dset.merge( e.source, e.target );
    }
  }
  return lastCost;
}

void dfs( Matrix G, int id, int u, int w, vector<int> &l, vector<int> &ID){
  ID[u] = id;
  l.push_back(u);
  for ( int v = 0; v < G.size(); v++ ){
    if ( ID[v] == -1 && G[u][v] < w ) dfs(G, id, v, w, l, ID);
  }
}

void printComps(vector<vector<int> > &comps){
  cout << comps.size() << " components" << endl;
  for ( int i = 0; i < comps.size(); i++ ){
    cout << i << ":";
    for ( int j = 0; j < comps[i].size(); j++ ) cout << " " << comps[i][j];
    cout << endl;
  }
}

void checkID(vector<vector<int> > &comps, vector<int> ID){
  int cnt = 0;
  for ( int i = 0; i < comps.size(); i++ ){
    for ( int j = 0; j < comps[i].size(); j++ ) {
      int v = comps[i][j];
      assert( ID[v] == i );
      cnt++;
    }
  }
  assert( cnt == ID.size() );
}

void initMatrix(Matrix &m){
  for ( int i = 0; i < m.size(); i++ ){
    for ( int j = 0; j < m[i].size(); j++ ) m[i][j] = 0;
  }
}


ll det(int n, int a[MAX][MAX], ll mod){
  rep(i, n) rep(j, n) assert( a[i][j] == a[j][i] );

  ll ans = 1;
  for (int i = 0; i < n; i++) {
    for (int j = i + 1; j < n; j++) {
      for (; a[j][i] != 0; ans = -ans) {
	ll r = a[i][i] / a[j][i];
	for (int k = i; k < n; k++) {
	  ll t = (a[i][k] - r * a[j][k]) % mod;
	  a[i][k] = a[j][k];
	  a[j][k] = t;
	}
      }
    }
    ans = ans * a[i][i] % mod;
  }
  return (ans + mod) % mod;
}

bool isConnected(Matrix M, vector<int> nodes){
  queue<int> Q;
  int d[MAX];
  for ( int i = 0; i < M.size(); i++ ) d[i] = INFTY;

  Q.push(nodes[0]);
  d[nodes[0]] = 0;
  int u, v;
  while( !Q.empty() ){
    u = Q.front(); Q.pop();
    for ( int v = 0; v < M.size(); v++ ){
      if ( M[u][v] && d[v] == INFTY ){
	d[v] = d[u] + 1;
	Q.push(v);
      }
    }
  }

  for ( int i = 0; i < nodes.size(); i++ ){
    if ( d[nodes[i]] == INFTY ) return false;
  }
  return true;
}

ll spCount(Matrix M, vector<int> IDM, vector<int> ID, int tgt, vector<int> nodes){
  int a[MAX][MAX];
  int deg[MAX];

  rep(i, M.size() ) deg[i] = 0;

  int ii = 0;
  rep(i, M.size() ){
    if ( IDM[ID[i]] != tgt ) continue;
    int jj = 0;
    rep(j, M.size() ){
      if ( IDM[ID[j]] != tgt ) continue;
      if ( i < j && M[i][j] ) {
	deg[ii]++;
	deg[jj]++;
      }
      a[ii][jj] = M[i][j]*(-1);

      jj++;
    }
    ii++;
  }

  rep(i, ii) a[i][i] = deg[i];

  assert(ii == nodes.size());

  ll d = det(ii-1, a, MOD);


  return d;
}

void solve(vector<Edge> edges, vector<vector<int> > G, vector<int> ID){
  int w = kruskal(G.size(), edges);

  cout << w << " ";
  vector<vector<int> > comps;
  rep(i, G.size()) ID[i] = -1;
  int id = 0;
  for ( int s = 0; s < G.size(); s++ ){
    if ( ID[s] == -1 ){
      vector<int> l;
      dfs(G, id++, s, w, l, ID);
      comps.push_back(l);
    }
  }
  
  vector<int> IDM; // comp index to A/B (0/1);
  Matrix A, B;
  A = B = G;
  int nc = comps.size();
  IDM.resize(nc);

  ll sum = 0;

  for ( int s = 1; s < (1<<nc)-1; s++ ){ // exclude 00000 and 11111
    vector<int> nodeA, nodeB;
    for ( int i = 0; i < nc; i++ ){
      IDM[i] = ((s & (1<<i))>0);
      if ( IDM[i] == 1 ) {
	for ( int j = 0; j < comps[i].size(); j++ ) nodeA.push_back(comps[i][j]);
      } else {
	for ( int j = 0; j < comps[i].size(); j++ ) nodeB.push_back(comps[i][j]);
      }
    }
    initMatrix(A);
    initMatrix(B);

    for ( int k = 0; k < edges.size(); k++ ){
      Edge e = edges[k];
      if ( IDM[ID[e.source]] == IDM[ID[e.target]] ){
	if ( IDM[ID[e.source]] == 1 ){
	  A[e.source][e.target] = A[e.target][e.source] = 1;
	} else {
	  assert( IDM[ID[e.source]] == 0);
	  B[e.source][e.target] = B[e.target][e.source] = 1;
	}
      }
    }

    ll cntA = spCount(A, IDM, ID, 1, nodeA);
    ll cntB = spCount(B, IDM, ID, 0, nodeB);

    sum = (sum + (cntA*cntB)%MOD ) % MOD;

  }

  cout << sum << endl;
}

int main(){
  int N, M, cost, source, target;
    
  cin >> N >> M;

  vector<vector<int> > G;
  vector<int> ID;
  
  rep(i, N) {
    vector<int> g;
    rep(j, N) g.push_back(INFTY);
    G.push_back(g);
    ID.push_back(-1);
  }
  
  vector<Edge> edges;
  for ( int i = 0; i < M; i++ ){
    cin >> source >> target >> cost;
    source--; target--;

    assert( 0 <= source && source < N);
    assert( 0 <= target && target < N);
    assert( 0 <= cost && cost <= 1000000000 );

    edges.push_back(Edge(source, target, cost));
    G[source][target] = G[target][source] = cost;
  }
  solve(edges, G, ID);

  return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 7104
final_rank: 12

code_snippet: #define _USE_MATH_DEFINES
#include <cstdio>
#include <iostream>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <complex>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <bitset>
#include <numeric>
#include <limits>
#include <climits>
#include <cfloat>
#include <functional>
#include <iterator>
using namespace std;

class UnionFindTree
{
private:
    int n;
    int groupNum;       // ??°??????????????°
    vector<int> parent; // ????????????
    vector<int> rank;   // ??¨??????????????????
    vector<int> num;    // ??°????????????????´???°
    int find(int i){
        if(parent[i] == i)
            return i;
        else
            return parent[i] = find(parent[i]);
    }
public:
    UnionFindTree(int n){ // ?????????????????????
        this->n = n;
        groupNum = n;
        parent.resize(n);
        for(int i=0; i<n; ++i)
            parent[i] = i;
        rank.assign(n, 0);
        num.assign(n, 1);
    }
    void unite(int a, int b){ // a??¨b?????°??????????????????
        if((a = find(a)) != (b = find(b))){
            if(rank[a] < rank[b]){
                parent[a] = b;
                num[b] += num[a];
            }
            else{
                parent[b] = a;
                if(rank[a] == rank[b])
                    ++ rank[a];
                num[a] += num[b];
            }
            -- groupNum;
        }
    }
    bool same(int a, int b){ // a??¨b?????°?????????????????????????????????
        return find(a) == find(b);
    }
    int getNum(){ // ??°??????????????°?????????
        return groupNum;
    }
    int getNum(int a){ // a?????°????????????????´???°?????????
        return num[find(a)];
    }
    void getGroup(vector<vector<int> >& group){ // ??°??????????§?????????????
        map<int, int> index;
        group.clear();
        for(int i=0; i<n; ++i){
            int a = find(i);
            if(index.find(a) == index.end()){
                index[a] = group.size();
                group.resize(group.size() + 1);
            }
            group[index[a]].push_back(i);
        }
    }
};

template <class T1>
class Operators
{
public:
    template <class T2>
    const T1 operator+(const T2& right) const{
        T1 ans = static_cast<const T1&>( *this );
        ans += right;
        return ans;
    }
    template <class T2>
    const T1 operator-(const T2& right) const{
        T1 ans = static_cast<const T1&>( *this );
        ans -= right;
        return ans;
    }
    template <class T2>
    const T1 operator*(const T2& right) const{
        T1 ans = static_cast<const T1&>( *this );
        ans *= right;
        return ans;
    }
    template <class T2>
    const T1 operator/(const T2& right) const{
        T1 ans = static_cast<const T1&>( *this );
        ans /= right;
        return ans;
    }
    bool operator!=(const T1& right) const{
        const T1& left = static_cast<const T1&>( *this );
        return !(left == right);
    }
};

class Mod : public Operators<Mod>
{
private:
    static const int MOD = 1000000007;
    long long a;
public:
    Mod(){
        a = 0;
    }
    Mod(long long x){
        a = (x % MOD + MOD) % MOD;
    }
    Mod& operator+=(const Mod& x){
        a = (a + x.a) % MOD;
        return *this;
    }
    Mod& operator-=(const Mod& x){
        a = (a - x.a + MOD) % MOD;
        return *this;
    }
    Mod& operator*=(const Mod& x){
        a = (a * x.a) % MOD;
        return *this;
    }
    Mod& operator/=(const Mod& x){ // ?????§?????????????°??????????MOD????´???°??§????????´???????????????
        int b = MOD - 2;
        long long c = x.a;
        while(b > 0){
            if(b & 1){
                a *= c;
                a %= MOD;
            }
            c *= c;
            c %= MOD;
            b >>= 1;
        }
        return *this;
    }
    bool operator==(const Mod& x) const{
        return a == x.a;
    }
    long long getValue(){
        return a;
    }
};

// ?????????????¨??????????
Mod determinant(vector<vector<Mod> > mat)
{
    const int n = mat.size();
    Mod ret = 1;
    for(int i=0; i<n; ++i){
        int tmp = -1;
        for(int j=i; j<n; ++j){
            if(mat[j][i] != 0){
                tmp = j;
                break;
            }
        }
        if(tmp == -1)
            return 0;
        if(tmp != i){
            mat[i].swap(mat[tmp]);
            ret *= -1;
        }

        for(int j=i+1; j<n; ++j){
            for(int k=n-1; k>=i; --k){
                mat[j][k] -= mat[i][k] * (mat[j][i] / mat[i][i]);
            }
        }
        ret *= mat[i][i];
    }
    return ret;
}

/***************************************************************************************************/
// ????????¨??????
//     ??????????????¢??????????????¨????????¨?????¨????????°????±???????
//
// graph : ????????°??????
/***************************************************************************************************/
Mod MatrixTreeTheorem(const vector<vector<int> >& graph)
{
    int n = graph.size();
    vector<vector<Mod> > mat(n-1, vector<Mod>(n-1, 0));
    for(int i=0; i<n-1; ++i){
        for(int j=0; j<n-1; ++j){
            mat[i][j] = -graph[i][j];
            if(i == j)
                mat[i][j] += accumulate(graph[i].begin(), graph[i].end(), Mod(0));
        }
    }
    return determinant(mat);
}

pair<int, int> solve(int n, const vector<tuple<int, int, int> >& edges)
{
    int m = edges.size();
    UnionFindTree uft(n);
    int dMax;
    for(int i=0; ; ++i){
        int d, s, t;
        tie(d, s, t) = edges[i];
        uft.unite(s, t);
        if(uft.getNum() == 1){
            dMax = d;
            break;
        }
    }

    uft = UnionFindTree(n);
    for(int i=0; i<m; ++i){
        int d, s, t;
        tie(d, s, t) = edges[i];
        if(d < dMax)
            uft.unite(s, t);
    }
    vector<vector<int> > group;
    uft.getGroup(group);

    int x = group.size();
    Mod ans = 0;
    for(int i=1; i<(1<<(x-1)); ++i){
        bitset<32> bs(i);
        vector<vector<int> > v(2);
        for(int j=0; j<x; ++j){
            int k = (bs[j] ? 0 : 1);
            copy(group[j].begin(), group[j].end(), back_inserter(v[k]));
        }

        Mod tmp = 1;
        for(int j=0; j<2; ++j){
            int y = v[j].size();
            vector<int> index(n, -1);
            for(int k=0; k<y; ++k)
                index[v[j][k]] = k;

            vector<vector<int> > graph(y, vector<int>(y, 0));
            for(int k=0; k<m; ++k){
                int d, s, t;
                tie(d, s, t) = edges[k];
                if(index[s] != -1 && index[t] != -1){
                    ++ graph[index[s]][index[t]];
                    ++ graph[index[t]][index[s]];
                }
            }
            tmp *= MatrixTreeTheorem(graph);
        }
        ans += tmp;
    }
    ans *= 2;

    return make_pair(dMax, (int)ans.getValue());
}

int main()
{
    int n, m;
    cin >> n >> m;
    vector<tuple<int, int, int> > edges(m);
    for(int i=0; i<m; ++i){
        int s, t, d;
        cin >> s >> t >> d;
        -- s;
        -- t;
        edges[i] = make_tuple(d, s, t);
    }
    sort(edges.begin(), edges.end());

    pair<int, int> ans = solve(n, edges);
    cout << ans.first << ' ' << ans.second << endl;

    return 0;
}
accuracy: 1
time_ms: 690
memory_kb: 3256
final_rank: 12

code_snippet: #define _USE_MATH_DEFINES
#include <cstdio>
#include <iostream>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <complex>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <bitset>
#include <numeric>
#include <limits>
#include <climits>
#include <cfloat>
#include <functional>
#include <iterator>
using namespace std;

class UnionFindTree
{
private:
    const int n;
    int groupNum;       // ??°??????????????°
    vector<int> parent; // ????????????
    vector<int> rank;   // ??¨??????????????????
    vector<int> num;    // ??°????????????????´???°
    int find(int i){
        if(parent[i] == i)
            return i;
        else
            return parent[i] = find(parent[i]);
    }
public:
    UnionFindTree(int n) : n(n){ // ?????????????????????
        groupNum = n;
        parent.resize(n);
        for(int i=0; i<n; ++i)
            parent[i] = i;
        rank.assign(n, 0);
        num.assign(n, 1);
    }
    void unite(int a, int b){ // a??¨b?????°??????????????????
        if((a = find(a)) != (b = find(b))){
            if(rank[a] < rank[b]){
                parent[a] = b;
                num[b] += num[a];
            }
            else{
                parent[b] = a;
                if(rank[a] == rank[b])
                    ++ rank[a];
                num[a] += num[b];
            }
            -- groupNum;
        }
    }
    bool same(int a, int b){ // a??¨b?????°?????????????????????????????????
        return find(a) == find(b);
    }
    int getNum(){ // ??°??????????????°?????????
        return groupNum;
    }
    int getNum(int a){ // a?????°????????????????´???°?????????
        return num[find(a)];
    }
    void getGroup(vector<vector<int> >& group){ // ??°??????????§?????????????
        map<int, int> index;
        group.clear();
        for(int i=0; i<n; ++i){
            int a = find(i);
            if(index.find(a) == index.end()){
                index[a] = group.size();
                group.resize(group.size() + 1);
            }
            group[index[a]].push_back(i);
        }
    }
};

template <class T1>
class Operators
{
public:
    template <class T2>
    const T1 operator+(const T2& right) const{
        T1 ans = static_cast<const T1&>( *this );
        ans += right;
        return ans;
    }
    template <class T2>
    const T1 operator-(const T2& right) const{
        T1 ans = static_cast<const T1&>( *this );
        ans -= right;
        return ans;
    }
    template <class T2>
    const T1 operator*(const T2& right) const{
        T1 ans = static_cast<const T1&>( *this );
        ans *= right;
        return ans;
    }
    template <class T2>
    const T1 operator/(const T2& right) const{
        T1 ans = static_cast<const T1&>( *this );
        ans /= right;
        return ans;
    }
    bool operator!=(const T1& right) const{
        const T1& left = static_cast<const T1&>( *this );
        return !(left == right);
    }
};

class Mod : public Operators<Mod>
{
private:
    static const int MOD = 1000000007;
    long long a;
public:
    Mod(){
        a = 0;
    }
    Mod(long long x){
        a = (x % MOD + MOD) % MOD;
    }
    Mod& operator+=(const Mod& x){
        a = (a + x.a) % MOD;
        return *this;
    }
    Mod& operator-=(const Mod& x){
        a = (a - x.a + MOD) % MOD;
        return *this;
    }
    Mod& operator*=(const Mod& x){
        a = (a * x.a) % MOD;
        return *this;
    }
    Mod& operator/=(const Mod& x){ // ?????§?????????????°??????????MOD????´???°??§????????´???????????????
        int b = MOD - 2;
        long long c = x.a;
        long long ans = 1;
        while(b > 0){
            if(b & 1){
                ans *= c;
                ans %= MOD;
            }
            c *= c;
            c %= MOD;
            b >>= 1;
        }
        a *= ans;
        a %= MOD;
        return *this;
    }
    bool operator==(const Mod& x) const{
        return a == x.a;
    }
    long long getValue(){
        return a;
    }
};

// ?????????????¨??????????
Mod determinant(vector<vector<Mod> > mat)
{
    const int n = mat.size();
    Mod ret = 1;
    for(int i=0; i<n; ++i){
        int tmp = -1;
        for(int j=i; j<n; ++j){
            if(mat[j][i] != 0){
                tmp = j;
                break;
            }
        }
        if(tmp == -1)
            return 0;
        if(tmp != i){
            mat[i].swap(mat[tmp]);
            ret *= -1;
        }

        for(int j=i+1; j<n; ++j){
            for(int k=n-1; k>=i; --k){
                mat[j][k] -= mat[i][k] * (mat[j][i] / mat[i][i]);
            }
        }
        ret *= mat[i][i];
    }
    return ret;
}

/***************************************************************************************************/
// ????????¨??????
//     ??????????????¢??????????????¨????????¨?????¨????????°????±???????
//
// graph : ????????°??????
/***************************************************************************************************/
Mod MatrixTreeTheorem(const vector<vector<int> >& graph)
{
    int n = graph.size();
    vector<vector<Mod> > mat(n-1, vector<Mod>(n-1, 0));
    for(int i=0; i<n-1; ++i){
        for(int j=0; j<n-1; ++j){
            mat[i][j] = -graph[i][j];
            if(i == j)
                mat[i][j] += accumulate(graph[i].begin(), graph[i].end(), Mod(0));
        }
    }
    return determinant(mat);
}

int main()
{
    int n, m;
    cin >> n >> m;
    vector<tuple<int, int, int> > edge(m);
    for(int i=0; i<m; ++i){
        int s, t, d;
        cin >> s >> t >> d;
        -- s;
        -- t;
        edge[i] = make_tuple(d, s, t);
    }
    sort(edge.begin(), edge.end());

    UnionFindTree uft(n);
    int dMax;
    for(int i=0; ; ++i){
        int d, s, t;
        tie(d, s, t) = edge[i];
        uft.unite(s, t);
        if(uft.getNum() == 1){
            dMax = d;
            break;
        }
    }

    UnionFindTree uft1(n);
    vector<pair<int, int> > v;
    for(int i=0; i<m; ++i){
        int d, s, t;
        tie(d, s, t) = edge[i];
        if(d < dMax)
            uft1.unite(s, t);
        else if(d == dMax)
            v.push_back(make_pair(s, t));
    }

    vector<vector<int> > group;
    vector<pair<int, int> > index(n);
    uft1.getGroup(group);
    for(unsigned i=0; i<group.size(); ++i){
        for(unsigned j=0; j<group[i].size(); ++j){
            index[group[i][j]] = make_pair(i, j);
        }
    }
    Mod tmp = 1;
    for(unsigned i=0; i<group.size(); ++i){
        vector<vector<int> > graph(group[i].size(), vector<int>(group[i].size(), 0));
        for(int j=0; j<m; ++j){
            int d, s, t;
            tie(d, s, t) = edge[j];
            if(index[s].first == i && index[t].first == i){
                ++ graph[index[s].second][index[t].second];
                ++ graph[index[t].second][index[s].second];
            }
        }
        tmp *= MatrixTreeTheorem(graph);
    }

    int a = v.size();
    Mod ans = 0;
    for(int i=0; i<(1<<a); ++i){
        bitset<32> bs(i);
        UnionFindTree uft2 = uft1;
        bool ok = true;
        for(int j=0; j<a; ++j){
            if(bs[j]){
                if(uft2.same(v[j].first, v[j].second)){
                    ok = false;
                    break;
                }
                uft2.unite(v[j].first, v[j].second);
            }
        }
        if(ok && uft2.getNum() == 2)
            ans += tmp * 2;
    }

    cout << dMax << ' ' << ans.getValue() << endl;

    return 0;
}
accuracy: 0.26666666666666666
time_ms: 30
memory_kb: 3280
final_rank: 14

code_snippet: #define _USE_MATH_DEFINES
#include <cstdio>
#include <iostream>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <complex>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <bitset>
#include <numeric>
#include <limits>
#include <climits>
#include <cfloat>
#include <functional>
#include <iterator>
using namespace std;

class UnionFindTree
{
private:
    const int n;
    int groupNum;       // ??°??????????????°
    vector<int> parent; // ????????????
    vector<int> rank;   // ??¨??????????????????
    vector<int> num;    // ??°????????????????´???°
    int find(int i){
        if(parent[i] == i)
            return i;
        else
            return parent[i] = find(parent[i]);
    }
public:
    UnionFindTree(int n) : n(n){ // ?????????????????????
        groupNum = n;
        parent.resize(n);
        for(int i=0; i<n; ++i)
            parent[i] = i;
        rank.assign(n, 0);
        num.assign(n, 1);
    }
    void unite(int a, int b){ // a??¨b?????°??????????????????
        if((a = find(a)) != (b = find(b))){
            if(rank[a] < rank[b]){
                parent[a] = b;
                num[b] += num[a];
            }
            else{
                parent[b] = a;
                if(rank[a] == rank[b])
                    ++ rank[a];
                num[a] += num[b];
            }
            -- groupNum;
        }
    }
    bool same(int a, int b){ // a??¨b?????°?????????????????????????????????
        return find(a) == find(b);
    }
    int getNum(){ // ??°??????????????°?????????
        return groupNum;
    }
    int getNum(int a){ // a?????°????????????????´???°?????????
        return num[find(a)];
    }
    void getGroup(vector<vector<int> >& group){ // ??°??????????§?????????????
        map<int, int> index;
        group.clear();
        for(int i=0; i<n; ++i){
            int a = find(i);
            if(index.find(a) == index.end()){
                index[a] = group.size();
                group.resize(group.size() + 1);
            }
            group[index[a]].push_back(i);
        }
    }
};

template <class T1>
class Operators
{
public:
    template <class T2>
    const T1 operator+(const T2& right) const{
        T1 ans = static_cast<const T1&>( *this );
        ans += right;
        return ans;
    }
    template <class T2>
    const T1 operator-(const T2& right) const{
        T1 ans = static_cast<const T1&>( *this );
        ans -= right;
        return ans;
    }
    template <class T2>
    const T1 operator*(const T2& right) const{
        T1 ans = static_cast<const T1&>( *this );
        ans *= right;
        return ans;
    }
    template <class T2>
    const T1 operator/(const T2& right) const{
        T1 ans = static_cast<const T1&>( *this );
        ans /= right;
        return ans;
    }
    bool operator!=(const T1& right) const{
        const T1& left = static_cast<const T1&>( *this );
        return !(left == right);
    }
};

class Mod : public Operators<Mod>
{
private:
    static const int MOD = 1000000007;
    long long a;
public:
    Mod(){
        a = 0;
    }
    Mod(long long x){
        a = (x % MOD + MOD) % MOD;
    }
    Mod& operator+=(const Mod& x){
        a = (a + x.a) % MOD;
        return *this;
    }
    Mod& operator-=(const Mod& x){
        a = (a - x.a + MOD) % MOD;
        return *this;
    }
    Mod& operator*=(const Mod& x){
        a = (a * x.a) % MOD;
        return *this;
    }
    Mod& operator/=(const Mod& x){ // ?????§?????????????°??????????MOD????´???°??§????????´???????????????
        int b = MOD - 2;
        long long c = x.a;
        long long ans = 1;
        while(b > 0){
            if(b & 1){
                ans *= c;
                ans %= MOD;
            }
            c *= c;
            c %= MOD;
            b >>= 1;
        }
        a *= ans;
        a %= MOD;
        return *this;
    }
    bool operator==(const Mod& x) const{
        return a == x.a;
    }
    long long getValue(){
        return a;
    }
};

// ?????????????¨??????????
Mod determinant(vector<vector<Mod> > mat)
{
    const int n = mat.size();
    Mod ret = 1;
    for(int i=0; i<n; ++i){
        int tmp = -1;
        for(int j=i; j<n; ++j){
            if(mat[j][i] != 0){
                tmp = j;
                break;
            }
        }
        if(tmp == -1)
            return 0;
        if(tmp != i){
            mat[i].swap(mat[tmp]);
            ret *= -1;
        }

        for(int j=i+1; j<n; ++j){
            for(int k=n-1; k>=i; --k){
                mat[j][k] -= mat[i][k] * (mat[j][i] / mat[i][i]);
            }
        }
        ret *= mat[i][i];
    }
    return ret;
}

/***************************************************************************************************/
// ????????¨??????
//     ??????????????¢??????????????¨????????¨?????¨????????°????±???????
//
// graph : ????????°??????
/***************************************************************************************************/
Mod MatrixTreeTheorem(const vector<vector<int> >& graph)
{
    int n = graph.size();
    vector<vector<Mod> > mat(n-1, vector<Mod>(n-1, 0));
    for(int i=0; i<n-1; ++i){
        for(int j=0; j<n-1; ++j){
            mat[i][j] = -graph[i][j];
            if(i == j)
                mat[i][j] += accumulate(graph[i].begin(), graph[i].end(), Mod(0));
        }
    }
    return determinant(mat);
}

int main()
{
    Mod hoge = Mod(-1) * (Mod(-1) / Mod(2));
    Mod piyo = Mod(-1) * Mod(-1) / Mod(2);

    int n, m;
    cin >> n >> m;
    vector<tuple<int, int, int> > edge(m);
    for(int i=0; i<m; ++i){
        int s, t, d;
        cin >> s >> t >> d;
        -- s;
        -- t;
        edge[i] = make_tuple(d, s, t);
    }
    sort(edge.begin(), edge.end());

    UnionFindTree uft(n);
    int dMax;
    for(int i=0; ; ++i){
        int d, s, t;
        tie(d, s, t) = edge[i];
        uft.unite(s, t);
        if(uft.getNum() == 1){
            dMax = d;
            break;
        }
    }

    UnionFindTree uft1(n);
    vector<pair<int, int> > v;
    for(int i=0; i<m; ++i){
        int d, s, t;
        tie(d, s, t) = edge[i];
        if(d < dMax)
            uft1.unite(s, t);
        else if(d == dMax)
            v.push_back(make_pair(s, t));
    }

    int a = v.size();
    Mod ans = 0;
    set<vector<vector<int> > > groupHistory;
    for(int i=0; i<(1<<a); ++i){
        bitset<5> bs(i);
        UnionFindTree uft2 = uft1;
        for(int j=0; j<a; ++j){
            if(bs[j])
                uft2.unite(v[j].first, v[j].second);
        }
        if(uft2.getNum() != 2)
            continue;

        vector<vector<int> > group;
        uft2.getGroup(group);
        sort(group[0].begin(), group[0].end());
        sort(group[1].begin(), group[1].end());
        sort(group.begin(), group.end());
        if(groupHistory.find(group) != groupHistory.end())
            continue;
        groupHistory.insert(group);

        vector<pair<int, int> > index(n);
        for(int j=0; j<2; ++j){
            for(unsigned k=0; k<group[j].size(); ++k){
                index[group[j][k]] = make_pair(j, k);
            }
        }

        Mod tmp = 2;
        for(int j=0; j<2; ++j){
            vector<vector<int> > graph(group[j].size(), vector<int>(group[j].size(), 0));
            for(int k=0; k<m; ++k){
                int d, s, t;
                tie(d, s, t) = edge[k];
                if(index[s].first == j && index[t].first == j){
                    ++ graph[index[s].second][index[t].second];
                    ++ graph[index[t].second][index[s].second];
                }
            }
            tmp *= MatrixTreeTheorem(graph);
        }
        ans += tmp;
    }

    cout << dMax << ' ' << ans.getValue() << endl;

    return 0;
}
accuracy: 0.26666666666666666
time_ms: 150
memory_kb: 3320
final_rank: 15