Problem:
Problem F:
There is No Alternative
ICPC (Isles of Coral Park City) consist of several beautiful islands.
The citizens requested construction of bridges between islands to resolve inconveniences of using boats between islands, and they demand that all the islands should be reachable from any other islands via one or more bridges.
The city mayor selected a number of pairs of islands, and ordered a building company to estimate the costs to build bridges between the pairs. With this estimate, the mayor has to decide the set of bridges to build, minimizing the total construction cost.
However, it is difficult for him to select the most cost-efficient set of bridges among those connecting all the islands. For example, three sets of bridges connect all the islands for the Sample Input 1. The bridges in each set are expressed by bold edges in Figure F.1.
Figure F.1. Three sets of bridges connecting all the islands for Sample Input 1
As the first step, he decided to build only those bridges which are contained in all the sets of bridges to connect all the islands and minimize the cost. We refer to such bridges as
no alternative bridges
. In Figure F.2, no alternative bridges are drawn as thick edges for the Sample Input 1, 2 and 3.
Figure F.2. No alternative bridges for Sample Input 1, 2 and 3
Write a program that advises the mayor which bridges are no alternative bridges for the given input.
Input
The input consists of a single test case.
$N$ $M$
$S_1$ $D_1$ $C_1$
.
.
.
$S_M$ $D_M$ $C_M$
The first line contains two positive integers $N$ and $M$. $N$ represents the number of islands and each island is identified by an integer 1 through $N$. $M$ represents the number of the pairs of islands between which a bridge may be built.
Each line of the next $M$ lines contains three integers $S_i$, $D_i$ and $C_i$ ($1 \leq i \leq M$) which represent that it will cost $C_i$ to build the bridge between islands $S_i$ and $D_i$. You may assume $3 \leq N \leq 500$, $N − 1 \leq M \leq min(50000, N(N − 1)/2)$, $1 \leq S_i < D_i \leq N$, and $1 \leq C_i \leq 10000$. No two bridges connect the same pair of two islands, that is, if $i \ne j$ and $S_i = S_j$, then $D_i \ne D_j$ . If all the candidate bridges are built, all the islands are reachable from any other islands via one or more bridges.
Output
Output two integers, which mean the number of no alternative bridges and the sum of their construction cost, separated by a space.
Sample Input 1
4 4
1 2 3
1 3 3
2 3 3
2 4 3
Sample Output 1
1 3
Sample Input 2
4 4
1 2 3
1 3 5
2 3 3
2 4 3
Sample Output 2
3 9
Sample Input 3
4 4
1 2 3
1 3 1
2 3 3
2 4 3
Sample Output 3
2 4
Sample Input 4
3 3
1 2 1
2 3 1
1 3 1
Sample Output 4
0 0

Submitted solutions:

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

struct Edge {
    int u, v, w, idx;
    bool inMST = false;
};

struct DSU {
    vector<int> parent, rankv;
    DSU(int n) {
        parent.resize(n+1);
        rankv.resize(n+1, 0);
        iota(parent.begin(), parent.end(), 0);
    }
    int find(int x) {
        if (parent[x] == x) return x;
        return parent[x] = find(parent[x]);
    }
    bool unite(int a, int b) {
        a = find(a); b = find(b);
        if (a == b) return false;
        if (rankv[a] < rankv[b]) swap(a, b);
        parent[b] = a;
        if (rankv[a] == rankv[b]) rankv[a]++;
        return true;
    }
};

const int INF = 1e9;
int N, M;
vector<Edge> edges;
vector<pair<int,int>> adj[505];
int par[505][10], mx[505][10], depthv[505];

void dfs(int u, int p, int w) {
    par[u][0] = p;
    mx[u][0] = w;
    for (auto [v, c] : adj[u]) {
        if (v == p) continue;
        depthv[v] = depthv[u] + 1;
        dfs(v, u, c);
    }
}

int getMaxEdge(int u, int v) {
    if (depthv[u] < depthv[v]) swap(u, v);
    int ans = 0;
    int diff = depthv[u] - depthv[v];
    for (int i = 9; i >= 0; i--) {
        if ((diff >> i) & 1) {
            ans = max(ans, mx[u][i]);
            u = par[u][i];
        }
    }
    if (u == v) return ans;
    for (int i = 9; i >= 0; i--) {
        if (par[u][i] != par[v][i]) {
            ans = max({ans, mx[u][i], mx[v][i]});
            u = par[u][i];
            v = par[v][i];
        }
    }
    ans = max({ans, mx[u][0], mx[v][0]});
    return ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> N >> M;
    edges.resize(M);
    for (int i = 0; i < M; i++) {
        cin >> edges[i].u >> edges[i].v >> edges[i].w;
        edges[i].idx = i;
    }

    // Step 1: Kruskal MST
    sort(edges.begin(), edges.end(), [](auto &a, auto &b) {
        return a.w < b.w;
    });
    DSU dsu(N);
    long long mst_cost = 0;
    for (auto &e : edges) {
        if (dsu.unite(e.u, e.v)) {
            e.inMST = true;
            mst_cost += e.w;
            adj[e.u].push_back({e.v, e.w});
            adj[e.v].push_back({e.u, e.w});
        }
    }

    // Step 2: Prepare LCA
    depthv[1] = 0;
    dfs(1, -1, 0);
    for (int j = 1; j < 10; j++) {
        for (int i = 1; i <= N; i++) {
            if (par[i][j-1] == -1) {
                par[i][j] = -1;
                mx[i][j] = mx[i][j-1];
            } else {
                par[i][j] = par[par[i][j-1]][j-1];
                mx[i][j] = max(mx[i][j-1], mx[par[i][j-1]][j-1]);
            }
        }
    }

    // Step 3: Check each MST edge if it can be replaced
    vector<int> isUnique(M, 0);
    for (auto &e : edges) {
        if (!e.inMST) continue;
        isUnique[e.idx] = 1; // assume unique
    }

    for (auto &e : edges) {
        if (e.inMST) continue;
        int maxEdge = getMaxEdge(e.u, e.v);
        // if same cost edge can replace some MST edge
        if (e.w == maxEdge) {
            // then the corresponding MST edge with that weight is not unique
            // we must unset all MST edges on that path having weight == maxEdge
            // (simple approach since N ≤ 500)
            // For simplicity, brute DFS path marking could also work.
        }
    }

    // For N ≤ 500, simpler re-check brute force method:
    // For each MST edge e, try removing it and see if MST cost increases.
    long long count = 0, sum = 0;
    for (auto &e : edges) {
        if (!e.inMST) continue;
        // remove e and compute alternative MST
        DSU dsu2(N);
        long long alt_cost = 0;
        int taken = 0;
        for (auto &f : edges) {
            if (f.idx == e.idx) continue;
            if (dsu2.unite(f.u, f.v)) {
                alt_cost += f.w;
                taken++;
                if (taken == N-1) break;
            }
        }
        if (taken < N-1 || alt_cost > mst_cost) {
            count++;
            sum += e.w;
        }
    }

    cout << count << " " << sum << "\n";
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 4408
final_rank: 6

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
#include <random>
#include <chrono>
using namespace std;
const long long LINF = 3e18 + 7;
const int MAX_N = 5000010;
const int MAX_W = 300002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
const int INF = 2147483647;
//using ll = long long;


// https://drken1215.hatenablog.com/entry/2020/11/04/221100

// kruskal
struct edge { long long u, v; long long cost; };
long long par[MAX_N];
long long rank1[MAX_N];
long long siz[MAX_N];

void init(long long n) {
	for (long long i = 0; i < n; i++) {
		par[i] = i;
		rank1[i] = 0;
		siz[i] = 1;
	}
}

long long find(long long x) {
	if (par[x] == x) {
		return x;
	}
	else {
		return par[x] = find(par[x]);
	}
}

void unite(long long x, long long y) {
	x = find(x);
	y = find(y);
	if (x == y) return;

	if (rank1[x] < rank1[y]) {
		par[x] = y;
		siz[y] += siz[x];
	}
	else {
		par[y] = x;
		siz[x] += siz[y];
		if (rank1[x] == rank1[y]) rank1[x]++;
	}
}

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

long long usize(long long x) {
	return siz[find(x)];
}

bool comp(const edge& e1, const edge& e2) {
	return e1.cost < e2.cost;
}

long long kruskal(int V, int E, vector<edge> es, vector<int> &ms) {
	init(V);
	long long res = 0;
	for (long long i = 0; i < E; i++) {
		edge e = es[i];
		if (!same(e.u, e.v)) {
			unite(e.u, e.v);
			res += e.cost;
			ms.push_back(i);
		}
	}
	return res;
}

int N, M;
int S[MAX_N], D[MAX_N];
long long C[MAX_N];


int main() {

	cin >> N >> M;
	
	vector<edge> es(M);
	vector<int> ms;
	for (int i = 0; i < M; i++) {
		cin >> S[i] >> D[i] >> C[i];
		S[i]--;
		D[i]--;
		es[i].u = S[i];
		es[i].v = D[i];
		es[i].cost = C[i];
	}

	sort(es.begin(), es.end(), comp);

	long long base = kruskal(N, M, es, ms);
	
	int cnt = 0;
	int ans = 0;
	for (int i = 0; i < ms.size(); i++) {
		int idx = ms[i];
		vector<edge> minus_edges;
		vector<int> minus_ms;
		for (int j = 0; j < M; j++) {
			if (idx == j) continue;
			minus_edges.push_back(es[j]);
		}	
		long long plus = kruskal(N, M - 1, minus_edges, minus_ms);
		if (plus != base) {
			cnt++;
			ans += es[idx].cost;
		}
		
	}

	cout << cnt << " " << ans << endl;
	
	return 0;
}
accuracy: 1
time_ms: 500
memory_kb: 17672
final_rank: 20

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

class UnionFind {
	public:
	vector<int> par;

	void init(int sz) {
		par.resize(sz, -1);
	}

	int root(int pos) {
		if (par[pos] == -1) return pos;
		par[pos] = root(par[pos]);
		return par[pos];
	}

	void unite(int u, int v) {
		u = root(u);
		v = root(v);
		if (u != v) par[u] = v;
	}

	bool same(int u, int v) {
		if (root(u) == root(v)) return true;
		return false;
	}
};

int main() {
	// step 1. input
	int N, M; cin >> N >> M;
	vector<tuple<int, int, int>> G(M);
	for (int i = 0; i < M; i++) {
		int a, b, c; cin >> a >> b >> c;
		G[i] = make_tuple(c, a, b);
	}
	sort(G.begin(), G.end());

	// step 2. get first answer
	vector<int> lst;
	long long init_cost = 0;
	if (true) {
		UnionFind UF; UF.init(N + 2);
		for (int i = 0; i < G.size(); i++) {
			auto [c, a, b] = G[i];
			if (UF.same(a, b) == false) {
				UF.unite(a, b);
				lst.push_back(i);
				init_cost += c;
			}
		}
	}

	// step 3. get second answer
	long long count = 0;
	long long cost = 0;
	for (int idx : lst) {
		UnionFind UF; UF.init(N + 2);
		long long cur_cost = 0;
		long long cur_count = 0;
		for (int i = 0; i < G.size(); i++) {
			if (i == idx) continue;
			auto [c, a, b] = G[i];
			if (UF.same(a, b) == false) {
				UF.unite(a, b);
				cur_cost += c;
				cur_count += 1;
			}
		}
		if (cur_count != N - 1 || cur_cost > init_cost) {
			count += 1;
			cost += get<0>(G[idx]);
		}
	}

	// step 4. output
	cout << count << " " << cost << endl;
	return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 3968
final_rank: 3

code_snippet: #include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

struct UnionFind {
    vector<int> par, siz;

    // 初期化
    UnionFind(int n) : par(n, -1), siz(n, 1) {}

    // 根を求める
    int root(int x) {
        if (par[x] == -1) return x; // xが根の場合はxを返す
        else return par[x] = root(par[x]);
    }

    // xとyが同じグループに属するかどうか (根が一致するかどうか)
    bool issame(int x, int y) { return root(x) == root(y); }

    // xを含むグループとyを含むグループとを併合する
    bool unite(int x, int y) {
        // x, yをそれぞれ根まで移動する
        x = root(x);
        y = root(y);

        // すでにグループの時は何もしない
        if (x == y) return false;

        // union by size (y側のサイズが小さくなるようにする)
        if (siz[x] < siz[y]) swap(x, y);

        // yをxの子とする
        par[y] = x;
        siz[x] += siz[y];
        return true;
    }

    // xを含むグループのサイズ
    int size(int x) { return siz[root(x)]; }
};

using Edge = pair<int, pair<int, int>>;

int main() {
    // 入力
    int N, M; // 頂点数と辺数
    cin >> N >> M;
    vector<Edge> edges(M); // 辺集合
    for (int i = 0; i < M; ++i) {
        int u, v, w; // wは重み
        cin >> u >> v >> w;
        u--, v--;
        edges[i] = Edge(w, make_pair(u, v));
    }

    // 各辺を、辺の重みが小さい順にソートする
    // pair はデフォルトで（第一要素, 第二要素）の辞書順比較
    sort(edges.begin(), edges.end());

    // 最小全域木に含まれる辺の集合
    vector<Edge> mst_edgs;  // minimun spannning tree edges

    // クラスカル法
    long long res = 0;
    UnionFind uf(N);
    for (int i = 0; i < M; ++i) {
        int w = edges[i].first;
        int u = edges[i].second.first;
        int v = edges[i].second.second;

        // 辺(u, v)の追加によってサイクルが形成されるときは追加しない
        if (uf.issame(u, v)) continue;

        // 辺(u, v)を追加する
        res += w;
        uf.unite(u, v);
        mst_edgs.push_back(edges[i]);
    }
    sort(mst_edgs.begin(), mst_edgs.end());
    // 最小全域木から辺iを抜いたものをシミュレーションする。計算量は N - 1
    vector<Edge> result;
    for (int i = 0; i < mst_edgs.size(); i++) {
        UnionFind uf_sim(N);
        // 計算量はmin(N - 2, M)なので高々辺の数 M * UnionFind.unite() = α(N)
        for (int j = 0; j < mst_edgs.size(); j++) {
            // 辺i以外の最小全域木の辺をくっつける(UnionFindに要素削除の操作はないのでi以外で新たに作る)
            if (i == j) continue;
            uf_sim.unite(mst_edgs[j].second.first, mst_edgs[j].second.second);
        } //
        // 辺i 以外の最小全域木の辺がつながっている状態が完成
        bool has_alternative = false;
        // 計算量は辺の数 M * UnionFind.unite() = α(N)
        for (int j = 0; j < M; ++j) {
            // mst_edgsに入っていたらスキップ
            if (binary_search(mst_edgs.begin(), mst_edgs.end(), edges[j]))
                continue;
            // 最小全域木の辺iと重みが等しくなければスキップ
            if (edges[j].first != mst_edgs[i].first) continue;
            if (uf_sim.unite(edges[j].second.first, edges[j].second.second)) {
                // カットセット以外のエッジだと上記uniteがサイクルを作ってしまって失敗する
                has_alternative = true;
                break;
            }
        }

        if (!has_alternative) result.push_back(mst_edgs[i]);
    }
    int res_first = result.size(); // 出力の1番目の要素 = no alt. bridge の本数
    int res_second = 0; // 出力の2番目の要素 = no alt. bridge の建設にかかる費用
    for (int i = 0; i < result.size(); ++i) {
        // std::cout << result[i].second.first + 1 << " "
        //           << result[i].second.second + 1 << std::endl;
        res_second+= result[i].first;
    }
    cout << res_first << " " << res_second << endl;
}
accuracy: 1
time_ms: 180
memory_kb: 3648
final_rank: 14

code_snippet: #include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define rep2(i, s, n) for (int i = (s); i < (int)(n); i++)
typedef long long ll;
using namespace std;

using Edge = pair<int, pair<int, int>>;

class UnionFind
{
private:
    vector<int> par;

public:
    UnionFind(int n)
    {
        par.resize(n, -1);
    }

    int root(int x)
    {
        if (par[x] < 0)
            return x;
        return par[x] = root(par[x]);
    }

    bool is_same(int x, int y)
    {
        return root(x) == root(y);
    }

    bool merge(int x, int y)
    {
        x = root(x);
        y = root(y);
        if (x == y)
            return false;
        if (par[x] > par[y])
            swap(x, y);
        par[x] += par[y];
        par[y] = x;
        return true;
    }

    int size(int x)
    {
        return -par[root(x)];
    }
};

int main()
{
    int n, m;
    cin >> n >> m;

    vector<Edge> e(m);
    int s, d, c;
    rep(i, m)
    {
        cin >> s >> d >> c;
        s--;
        d--;
        e[i] = Edge(c, make_pair(s, d));
    }

    sort(e.begin(), e.end());

    auto calc = [&](vector<int> &res, int id = -1) -> ll
    {
        ll cost = 0;
        res.clear();

        UnionFind uf(n);
        rep(i, m)
        {
            if (i == id)
                continue;
            int u = e[i].second.first;
            int v = e[i].second.second;
            if (uf.is_same(u, v))
                continue;
            res.push_back(i);
            cost += e[i].first;
            uf.merge(u, v);
        }

        if (res.size() < n - 1)
            return (1LL << 60);
        return cost;
    };

    vector<int>
        mst;
    ll base = calc(mst);

    vector<int> dummy;
    ll res = 0, num = 0;
    for (auto id : mst)
    {
        if (calc(dummy, id) > base)
        {
            num++;
            res += e[id].first;
        }
    }

    cout << num << " " << res << endl;
}
accuracy: 1
time_ms: 100
memory_kb: 3604
final_rank: 5

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

using ll = long long;
using pii = pair<int, int>;

struct UnionFind {
    vector<int> p;
    UnionFind(int n) {
        p.resize(n, -1);
    }

    int root(int x) {
        if (p[x] < 0) return x;
        else return p[x] = root(p[x]);
    }

    void unite(int x, int y) {
        x = root(x);
        y = root(y);
        if (x == y) return;
        if (p[x] > p[y]) swap(x, y);
        p[x] += p[y];
        p[y] = x;
    }

    int size(int x) {
        return -p[root(x)];
    }

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

int main() {
    ios::sync_with_stdio(0); cin.tie(0);
    int n, m;
    cin >> n >> m;
    vector<int> s(m), d(m), c(m);
    for (int i = 0; i < m; i++) {
        cin >> s[i] >> d[i] >> c[i];
        s[i]--; d[i]--;
    }
    vector<bool> used(m);
    vector<pii> edges(m);
    for (int i = 0; i < m; i++) {
        edges[i] = {c[i], i};
    }
    sort(edges.begin(), edges.end());
    int mincost = 0;
    UnionFind uf(n);
    for (int i = 0; i < m; i++) {
        auto [cost, idx] = edges[i];
        if (!uf.same(s[idx], d[idx])) {
            mincost += cost;
            uf.unite(s[idx], d[idx]);
            used[idx] = true;
        }
    }

    int ans = 0, ans2 = 0;
    for (int i = 0; i < m; i++) {
        if (!used[i]) continue;
        UnionFind uf(n);
        int ncost = 0;
        for (int j = 0; j < m; j++) {
            auto [cost, idx] = edges[j];
            if (idx == i) continue;
            if (!uf.same(s[idx], d[idx])) {
                ncost += cost;
                uf.unite(s[idx], d[idx]);
            }
        }
        if (uf.size(0) != n || ncost > mincost) {
            ans++;
            ans2 += c[i];
        }
    }
    cout << ans << " " << ans2 << endl;
}
accuracy: 1
time_ms: 100
memory_kb: 4136
final_rank: 12

code_snippet: #include <iostream>
#include <vector>
#include <cassert>
#include <algorithm>
#include <utility>
#include <numeric>
#include <tuple>

struct DSU {
    int n;
    std::vector<int> par, size;
    DSU(int N) : n{N}, par(N), size(N, 1) {
        std::iota(par.begin(), par.end(), 0);
    }
    int leader(int v) {
        return v == par[v] ? v : (par[v] = leader(par[v]));
    }
    void merge(int u, int v) {
        u = leader(u);
        v = leader(v);
        if (u != v) {
            if (size[u] < size[v]) std::swap(u, v);
            par[v] = u;
            size[u] += size[v];
        }
    }
};

int number_bridge(int N, std::vector<std::pair<int, int>> E) {
    for (int i{} ; i < (int)E.size() ; i++) if (E[i].first > E[i].second) {
        std::swap(E[i].first, E[i].second);
    }
    std::sort(E.begin(), E.end());
    std::vector<int> low(N), in(N, -1);
    std::vector<bool> bridge(E.size());
    std::vector<std::vector<std::pair<int, int>>> g(N);
    for (int i{} ; i < (int)E.size() ; i++) {
        auto [u, v]{E[i]};
        g[u].push_back({ v, i });
        g[v].push_back({ u, i });
    }
    int t{};
    auto dfs{[&](auto dfs, int v, int p) -> void {
        low[v] = in[v] = t++;
        for (const auto& [x, i] : g[v]) {
            if (in[x] == -1) {
                dfs(dfs, x, v);
                low[v] = std::min(low[v], low[x]);
                if (low[x] > in[v]) bridge[i] = true;
            }
            else if (x != p) {
                low[v] = std::min(low[v], in[x]);
            }
        }
    }};
    for (int i{} ; i < N ; i++) if (in[i] == -1) {
        dfs(dfs, i, -1);
    }
    // std::cout << "HELLO" << std::endl;
    // for (auto [u, v] : E) std::cout << u << ' ' << v << std::endl;
    for (int i{}, j{} ; i < (int)E.size() ; i = j) {
        while (j < (int)E.size() and E[i] == E[j]) j++;
        if (j - i >= 2) for (int k{i} ; k < j ; k++) bridge[k] = false;
    }
    int res{(int)std::count(bridge.begin(), bridge.end(), true)};
    // std::cout << res << std::endl;
    return res;
}

std::pair<int, long long> solve(int N, int M, std::vector<std::tuple<int, int, int>> E) {
    std::sort(E.begin(), E.end());
    DSU dsu(N);
    int cnt{};
    long long ans{};
    for (int i{}, j{} ; i < (int)E.size() ; ) {
        std::vector<std::pair<int, int>> cand;
        while (j < (int)E.size() and std::get<0>(E[i]) == std::get<0>(E[j])) {
            auto [c, s, d]{E[j]};
            s = dsu.leader(s);
            d = dsu.leader(d);
            if (s != d) cand.push_back({ s, d });
            j++;
        }
        int cur{number_bridge(N, cand)};
        cnt += cur;
        ans += (long long)std::get<0>(E[i]) * cur;
        while (i < j) {
            auto [c, s, d]{E[i]};
            dsu.merge(s, d);
            i++;
        }
    }
    return { cnt, ans };
}

int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);

    int N, M;
    std::cin >> N >> M;
    std::vector<std::tuple<int, int, int>> E(M);
    for (auto& [c, s, d] : E) {
        std::cin >> s >> d >> c;
        s--; d--;
    }
    auto [a, b]{solve(N, M, E)};
    std::cout << a << ' ' << b << '\n';
}
accuracy: 1
time_ms: 10
memory_kb: 6256
final_rank: 15

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

/**
 * @brief template
 */

struct UnionFind{
    vector<int> par; int n;
    UnionFind(){}
    UnionFind(int _n):par(_n,-1),n(_n){}
    int root(int x){return par[x]<0?x:par[x]=root(par[x]);}
    bool same(int x,int y){return root(x)==root(y);}
    int size(int x){return -par[root(x)];}
    bool unite(int x,int y){
        x=root(x),y=root(y); if(x==y)return false;
        if(size(x)>size(y))swap(x,y);
        par[y]+=par[x]; par[x]=y; n--; return true;
    }
};

/**
 * @brief Union Find
 */

int main() {
    int N, M;
    cin >> N >> M;
    vector<int> A(M), B(M), C(M);
    rep(i,0,M) cin >> A[i] >> B[i] >> C[i], A[i]--, B[i]--;
    vector<int> ord(M);
    iota(ALL(ord),0);
    sort(ALL(ord),[&](int i, int j){return C[i]<C[j];});
    vector<bool> ANS(M,false);
    auto Calc = [&](int NG) -> int {
        UnionFind UF(N);
        int Count = 0, Sum = 0;
        for (int i : ord) {
            if (i == NG) continue;
            if (!UF.same(A[i],B[i])) {
                Count++;
                Sum += C[i];
                UF.unite(A[i],B[i]);
                if (NG == -1) ANS[i] = true;
            }
        }
        if (Count < N-1) return inf;
        else return Sum;
    };
    int MIN = Calc(-1);
    rep(i,0,M) {
        if (ANS[i]) {
            if (MIN == Calc(i)) ANS[i] = false;
        }
    }
    int ANS2 = 0, ANS3 = 0;
    rep(i,0,M) if (ANS[i]) ANS2++, ANS3 += C[i];
    cout << ANS2 << ' ' << ANS3 << endl;
}
accuracy: 1
time_ms: 160
memory_kb: 3872
final_rank: 13

code_snippet: #include <bits/stdc++.h>

using ll = long long;
//  [l, r)
#define rep(i, l, r) for (ll i = (l); i < (r); i++)
#define rrep(i, r, l) for (ll i = (r); i-- > (l);)
template <class T, class U>
bool chmin(T& lhs, U rhs) {
    return lhs > rhs ? (lhs = rhs, true) : false;
}
template <class T, class U>
bool chmax(T& lhs, U rhs) {
    return lhs < rhs ? (lhs = rhs, true) : false;
}
using namespace std;

struct UF {
    vector<int> data;
    UF(int n): data(n, -1) {}
    int find(int x) {
        if (data[x] < 0) return x;
        return data[x] = find(data[x]);
    }
    bool merge(int lhs, int rhs) {
        lhs = find(lhs);
        rhs = find(rhs);
        if (lhs == rhs) return false;
        if (data[lhs] < data[rhs]) swap(lhs, rhs);
        data[rhs] += data[lhs];
        data[lhs] = rhs;
        return true;
    }
};

int main() {
    int n, m;
    cin >> n >> m;
    vector<tuple<int, int, int>> edges;
    rep(i, 0, m) {
        int u, v, w;
        cin >> u >> v >> w;
        u--; v--;
        edges.emplace_back(u, v, w);
    }
    vector<bool> removed(m);
    int cost_sum = 0;
    int num = 0;
    const int NONE = -1;

    auto MST = [&]() {
        vector<int> mst_eid;
        vector<int> min_eid(n);
        UF uf(n);
        int cost = 0;
        while ((int)mst_eid.size() != n - 1) {
            min_eid.assign(n, NONE);
            rep(i, 0, m) {
                if (removed[i]) continue;
                auto [u, v, w] = edges[i];
                int uu = uf.find(u);
                int vv = uf.find(v);
                if (uu == vv) continue;
                if (min_eid[uu] == NONE || std::get<2>(edges[min_eid[uu]]) > w) min_eid[uu] = i;
                if (min_eid[vv] == NONE || std::get<2>(edges[min_eid[vv]]) > w) min_eid[vv] = i;
            }
            int temp = 0;
            rep(i, 0, n) {
                if (min_eid[i] == NONE) continue;
                auto [u, v, w] = edges[min_eid[i]];
                if (uf.merge(u, v)) {
                    cost += w;
                    temp++;
                    mst_eid.push_back(min_eid[i]);
                }
            }
            if (temp == 0) break;
        }
        return std::pair(cost, mst_eid);
    };

    auto [mst_cost, target_eid] = MST();
    for (auto eid: target_eid) {
        removed[eid] = true;
        auto [cur_cost, cur_eid] = MST();
        if (cur_cost != mst_cost || (int)cur_eid.size() != n - 1) {
            cost_sum += std::get<2>(edges[eid]);
            num++;
        }
        removed[eid] = false;
    }
    std::cout << num << ' ' << cost_sum << '\n';
}
accuracy: 1
time_ms: 790
memory_kb: 4104
final_rank: 16

code_snippet: #pragma GCC optimize("O3")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("avx,avx2")
#include <bits/stdc++.h>
#define INF 1000000001LL
#define LNF 1000000000000000001LL
#define MOD 1000000007LL
#define MAX 502
#define long long long
#define all(x) x.begin(),x.end()
using namespace std;


//UnionFind
class UnionFind
{
public:
    vector<int> parent;
    vector<int> cnt;
    void init(int size)
    {
        parent.resize(size);
        cnt.resize(size);
        for(int i = 0; i<size; i++)
        {
            parent[i] = i;
            cnt[i] = 1;
        }
    }
    int find(int x)
    {
        if(parent[x] == x)
            return x;
        return parent[x] = find(parent[x]);
    }
    void merge(int x, int y)
    {
        x = find(x);
        y = find(y);
        if(x == y)
            return;
        if(x < y)
        {
            parent[y] = x;
            cnt[x]+=cnt[y];
        }
        else
        {
            parent[x] = y;
            cnt[y]+=cnt[x];
        }
    }
    bool isUnion(int x, int y)
    {
        x = find(x);
        y = find(y);
        if(x == y)
            return true;
        return false;
    }
};

vector<pair<long,pair<int,int>>> edge;
vector<int> p;
long getMST(int n, int m, int x)
{
    UnionFind uf;
    uf.init(n+1);
    long res = 0;
    p.clear();
    for(int i = 0; i<m; i++)
    {
        if(i == x)
            continue;
        long c = edge[i].first;
        int u = edge[i].second.first;
        int v = edge[i].second.second;

        if(uf.isUnion(u,v))
            continue;
        p.push_back(i);
        res+=c;
        uf.merge(u,v);
    }
    return res;
}

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

    int n,m;
    cin >> n >> m;

    

    for(int i = 0; i<m; i++)
    {
        int a,b,c;
        cin >> a >> b >> c;

        edge.push_back({c,{a,b}});
    }

    sort(all(edge));

    long v = getMST(n,m,-1);
    vector<int> arr = p;
    int res = 0;
    long resv = 0;
    for(int i = 0; i<arr.size(); i++)
    {
        long x = getMST(n,m,arr[i]);

        if(x != v)
        {
            res++;
            resv+=edge[arr[i]].first;
        }
    }
    cout << res << " " << resv << endl;


    return 0;
}
accuracy: 1
time_ms: 80
memory_kb: 4168
final_rank: 10

code_snippet: #line 2 "cp-library/src/cp-template.hpp"
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;
using uint = unsigned int;
using ull  = unsigned long long;
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128_t;
template < class T > bool chmin(T& a, T b) { if(a > b) { a = b; return true; } return false; }
template < class T > bool chmax(T& a, T b) { if(a < b) { a = b; return true; } return false; }
template < class T, class U > T ceil (T x, U y) { return (x > 0 ? (x + y - 1) / y :           x / y); }
template < class T, class U > T floor(T x, U y) { return (x > 0 ?           x / y : (x - y + 1) / y); }
int popcnt(i32 x) { return __builtin_popcount(x); }
int popcnt(u32 x) { return __builtin_popcount(x); }
int popcnt(i64 x) { return __builtin_popcountll(x); }
int popcnt(u64 x) { return __builtin_popcountll(x); }

#line 2 "cp-library/src/utility/rep_itr.hpp"
template < class T > struct itr_rep {
    T i, d;
    constexpr itr_rep(const T i) noexcept : i(i), d(1) {}
    constexpr itr_rep(const T i, const T d) noexcept : i(i), d(d) {}
    void operator++() noexcept { i += d; }
    constexpr int operator*() const noexcept { return i; }
    constexpr bool operator!=(const itr_rep x) const noexcept { return d > 0 ? i < x.i : i > x.i; }
};

template < class T > struct rep {
    const itr_rep< T > s, t;
    constexpr rep(const T t) noexcept : s(0), t(t) {}
    constexpr rep(const T s, const T t) noexcept : s(s), t(t) {}
    constexpr rep(const T s, const T t, const T d) noexcept : s(s, d), t(t, d) {}
    constexpr auto begin() const noexcept { return s; }
    constexpr auto end  () const noexcept { return t; }
};

template < class T > struct revrep {
    const itr_rep < T > s, t;
    constexpr revrep(const T t) noexcept : s(t - 1, -1), t(-1, -1) {}
    constexpr revrep(const T s, const T t) noexcept : s(t - 1, -1), t(s - 1, -1) {}
    constexpr revrep(const T s, const T t, const T d) noexcept : s(t - 1, -d), t(s - 1, -d) {}
    constexpr auto begin() const noexcept { return s; }
    constexpr auto end  () const noexcept { return t; }
};
#line 3 "cp-library/src/utility/io.hpp"

/* 128bit integer */
istream& operator>>(istream& is, i128& x) {
    std::string s; is >> s;
    int pm = (s[0] == '-');
    x = 0;
    for(int i : rep(pm, int(s.size()))) x = x * 10 + (s[i] - '0');
    if(pm) x *= -1;
    return is;
}
ostream& operator<<(ostream& os, const i128& x) {
    if(x == 0) return os << '0';
    i128 y = x;
    if(y < 0) { os << '-'; y *= -1; }
    std::vector<int> ny;
    while(y > 0) { ny.push_back(y % 10); y /= 10; }
    for(int i : revrep(ny.size())) os << ny[i];
    return os;
}

template < class S, class T > istream& operator>>(istream& is,       std::pair< S, T >& x) { is >> x.first        >> x.second; return is; }
template < class S, class T > ostream& operator<<(ostream& os, const std::pair< S, T >& x) { os << x.first << " " << x.second; return os; }

namespace scanner {
    struct sca {
        template < class T > operator T() {
            T s; std::cin >> s; return s;
        }
    };
    struct vec {
        int n;
        vec(int n) : n(n) {}
        template < class T > operator std::vector< T >() {
            std::vector< T > v(n);
            for(T& x : v) std::cin >> x;
            return v;
        }
    };
    struct mat {
        int h, w;
        mat(int h, int w) : h(h), w(w) {}
        template < class T > operator std::vector< std::vector< T > >() {
            std::vector m(h, std::vector< T >(w));
            for(std::vector< T >& v : m) for(T& x : v) std::cin >> x;
            return m;
        }
    };
    struct speedup {
        speedup() {
            std::cin.tie(0);
            std::ios::sync_with_stdio(0);
        }
    } speedup_instance;
}
scanner::sca in() { return scanner::sca(); }
scanner::vec in(int n) { return scanner::vec(n); }
scanner::mat in(int h, int w) { return scanner::mat(h, w); }

namespace printer {
    void precision(int d) { std::cout << std::fixed << std::setprecision(d); }
    void flush() { std::cout.flush(); }
}

template < class T >
ostream& operator<<(ostream& os, const std::vector< T > a) {
    int n = a.size();
    for(int i : rep(n)) { os << a[i]; if(i != n - 1) os << ' '; }
    return os;
}

int print() { std::cout << '\n'; return 0; }
template < class head, class... tail > int print(head&& h, tail&&... t) {
    std::cout << h; if(sizeof...(tail)) std::cout << ' ';
    return print(std::forward<tail>(t)...);
}
template < class T > int print_n(const std::vector< T > a) {
    int n = a.size();
    for(int i : rep(n)) std::cout << a[i] << "\n";
    return 0;
}


#line 2 "cp-library/src/utility/key_val.hpp"

template < class K, class V >
struct key_val {
    K key; V val;
    key_val() {}
    key_val(K key, V val) : key(key), val(val) {}
    template < std::size_t Index >
    std::tuple_element_t< Index, key_val >& get() {
        if constexpr (Index == 0) return key;
        if constexpr (Index == 1) return val;
    }
};

namespace std {

template < class K, class V > struct tuple_size < key_val< K, V > > : integral_constant< size_t, 2 > {};
template < class K, class V > struct tuple_element < 0, key_val< K, V > > { using type = K; };
template < class K, class V > struct tuple_element < 1, key_val< K, V > > { using type = V; };

}
#line 2 "cp-library/src/utility/vec_op.hpp"
template < class T > key_val< int, T > max_of(const vector< T >& a) {
    int i = std::max_element(a.begin(), a.end()) - a.begin();
    return {i, a[i]};
}
template < class T > key_val< int, T > min_of(const vector< T >& a) {
    int i = std::min_element(a.begin(), a.end()) - a.begin();
    return {i, a[i]};
}
template < class S, class T > S sum_of(const vector< T >& a) {
    S sum = 0;
    for(const T x : a) sum += x;
    return sum;
}
template < class S, class T > vector< S > freq_of(const vector< T >& a, T L, T R) {
    vector< S > res(R - L, S(0));
    for(const T x : a) res[x - L] += 1;
    return res;
}
template < class S, class T > struct prefix_sum {
    vector< S > s;
    prefix_sum(const vector< T >& a) : s(a) {
        s.insert(s.begin(), S(0));
        for(int i : rep(a.size())) s[i + 1] += s[i];
    }
    // [L, R)
    S sum(int L, int R) { return s[R] - s[L]; }
};
#line 3 "cp-library/src/utility/heap.hpp"

template < class T > using heap_min = std::priority_queue< T, std::vector< T >, std::greater< T > >;
template < class T > using heap_max = std::priority_queue< T, std::vector< T >, std::less< T > >;

#line 27 "cp-library/src/cp-template.hpp"

#line 1 "cp-library/src/algorithm/bin_search.hpp"
template < class T, class F >
T bin_search(T ok, T ng, F f) {
    while(abs(ng - ok) > 1) {
        T mid = (ok + ng) / 2;
        (f(mid) ? ok : ng) = mid;
    }
    return ok;
}

template < class T, class F >
T bin_search_real(T ok, T ng, F f, int step = 80) {
    while(step--) {
        T mid = (ok + ng) / 2;
        (f(mid) ? ok : ng) = mid;
    }
    return ok;
}
#line 2 "cp-library/src/algorithm/argsort.hpp"

template < class T > std::vector< int > argsort(const std::vector< T > &a) {
    std::vector< int > ids((int)a.size());
    std::iota(ids.begin(), ids.end(), 0);
    std::sort(ids.begin(), ids.end(), [&](int i, int j) {
        return a[i] < a[j] || (a[i] == a[j] && i < j);
    });
    return ids;
}
#line 1 "macro.hpp"
namespace macro {

using size_type = int;
template < class container > void  sort(container& a) { std::sort(std:: begin(a), std:: end(a)); }
template < class container > void rsort(container& a) { std::sort(std::rbegin(a), std::rend(a)); }
template < class container > void reverse(container& a) { std::reverse(std::begin(a), std::end(a)); }
template < class container > void unique(container& a) {
    std::sort(std::begin(a), std::end(a));
    a.erase(std::unique(std::begin(a), std::end(a)), std::end(a));
}
template < class container > container  sorted(const container& a) { container b = a;  sort(b); return std::move(b); }
template < class container > container rsorted(const container& a) { container b = a; rsort(b); return std::move(b); }
template < class container, class compare > void sort(container& a, const compare& cmp) { std::sort(std::begin(a), std::end(a), cmp); }
template < class container, class compare > container sorted(const container& a, const compare& cmp) { container b = a; sort(b, cmp); return std::move(b); }
template < class container, class value > size_type lower_bound(const container& a, const value& x) { return std::lower_bound(std::begin(a), std::end(a), x) - std::begin(a); }
template < class container, class value > size_type upper_bound(const container& a, const value& x) { return std::upper_bound(std::begin(a), std::end(a), x) - std::begin(a); }

const std::vector<std::pair<size_type, size_type>> dir4 = { {+1,  0}, {-1,  0}, { 0, +1}, { 0, -1} };
const std::vector<std::pair<size_type, size_type>> dir8 = { {-1, -1}, {-1,  0}, {-1, +1}, { 0, -1}, { 0, +1}, {+1, -1}, {+1,  0}, {+1, +1} };

#ifdef _DEBUG
#define debug(x) std::cout << "[" << __LINE__ << "] " << #x << ": " << x << std::endl
#else
#define debug(x)
#endif

template < class container > void concat(container& a, const container& b) {
    a.insert(std::end(a), std::begin(b), std::end(b));
}
std::vector<size_type> iota(const size_type n) {
    std::vector<size_type> I(n);
    std::iota(std::begin(I), std::end(I), 0);
    return I;
}
template < class container > std::vector<size_type> sort_idx(const container& a) {
    const size_type n = a.size();
    std::vector<size_type> I = iota(n);
    std::sort(std::begin(I), std::end(I), [&](size_type i, size_type j) { return a[i] < a[j] or (a[i] == a[j] and i < j); });
    return I;
}
template < class container, class compare > std::vector<size_type> sort_idx(const container& a, const compare& cmp) {
    const size_type n = a.size();
    std::vector<size_type> I = iota(n);
    std::sort(std::begin(I), std::end(I), [&](size_type i, size_type j) { return cmp(a[i], a[j]) or (a[i] == a[j] and i < j); });
    return std::move(I);
}

struct grid {
    using size_type = int;
    size_type H, W;
    grid(const size_type H, const size_type W) : H(H), W(W) {}
    bool contains(const size_type i, const size_type j) {
        return 0 <= i and i < H and 0 <= j and j < W;
    }
};

} // namespace macro

using namespace macro;
#line 1 "cp-library/src/data_structure/union_find.hpp"
class union_find {
  public:
    union_find(int n) : data(n, -1) {}
    int unite(int x, int y) {
        x = root(x), y = root(y);
        if(x != y) {
            if(size(x) < size(y)) swap(x, y);
            data[x] += data[y];
            return data[y] = x;
        }
        return -1;
    }
    int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
    int size(int x) { return -data[root(x)]; }
    bool same(int x, int y) { return root(x) == root(y); }

  private:
    vector<int> data;
};
#line 4 "A.cpp"

int main() {
    int N = in(), M = in();
    vector<tuple<int,int,int>> E(M);
    for(auto &[u, v, c] : E) u = in(), v = in(), c = in(), u--, v--;

    vector<int> mst;
    int mst_cost = 0;
    sort(E, [&](auto I, auto J) { return get<2>(I) < get<2>(J); });
    {
        union_find uf(N);
        for(int i : rep(M)) {
            auto [u, v, c] = E[i];
            if(uf.unite(u, v) != -1) {
                mst.push_back(i);
                mst_cost += c;
            }
        }
    }

    int ans_cnt = 0;
    int ans_sum = 0;
    for(int target : mst) {
        union_find uf(N);
        int cost = 0;
        for(int i : rep(M)) if(i != target) {
            auto [u, v, c] = E[i];
            if(uf.unite(u, v) != -1) {
                cost += c;
            }
        }
        if(mst_cost != cost) {
            ans_cnt += 1;
            ans_sum += get<2>(E[target]);
        }
    }

    print(ans_cnt, ans_sum);
}
accuracy: 1
time_ms: 60
memory_kb: 3608
final_rank: 1

code_snippet: // #define _GLIBCXX_DEBUG
#pragma GCC optimize("O2,no-stack-protector,unroll-loops,fast-math")
#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < int(n); i++)
#define per(i, n) for (int i = (n)-1; 0 <= i; i--)
#define rep2(i, l, r) for (int i = (l); i < int(r); i++)
#define per2(i, l, r) for (int i = (r)-1; int(l) <= i; i--)
#define each(e, v) for (auto& e : v)
#define MM << " " <<
#define pb push_back
#define eb emplace_back
#define all(x) begin(x), end(x)
#define rall(x) rbegin(x), rend(x)
#define sz(x) (int)x.size()
template <typename T> void print(const vector<T>& v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
    if (v.empty()) cout << '\n';
}
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
template <typename T> bool chmax(T& x, const T& y) {
    return (x < y) ? (x = y, true) : false;
}
template <typename T> bool chmin(T& x, const T& y) {
    return (x > y) ? (x = y, true) : false;
}
template <class T>
using minheap = std::priority_queue<T, std::vector<T>, std::greater<T>>;
template <class T> using maxheap = std::priority_queue<T>;
template <typename T> int lb(const vector<T>& v, T x) {
    return lower_bound(begin(v), end(v), x) - begin(v);
}
template <typename T> int ub(const vector<T>& v, T x) {
    return upper_bound(begin(v), end(v), x) - begin(v);
}
template <typename T> void rearrange(vector<T>& v) {
    sort(begin(v), end(v));
    v.erase(unique(begin(v), end(v)), end(v));
}

// __int128_t gcd(__int128_t a, __int128_t b) {
//     if (a == 0)
//         return b;
//     if (b == 0)
//         return a;
//     __int128_t cnt = a % b;
//     while (cnt != 0) {
//         a = b;
//         b = cnt;
//         cnt = a % b;
//     }
//     return b;
// }

struct Union_Find_Tree {
    vector<int> data;
    const int n;
    int cnt;
 
    Union_Find_Tree(int n) : data(n, -1), n(n), cnt(n) {}
 
    int root(int x) {
        if (data[x] < 0) return x;
        return data[x] = root(data[x]);
    }
 
    int operator[](int i) { return root(i); }
 
    bool unite(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false;
        if (data[x] > data[y]) swap(x, y);
        data[x] += data[y], data[y] = x;
        cnt--;
        return true;
    }
 
    int size(int x) { return -data[root(x)]; }
 
    int count() { return cnt; };
 
    bool same(int x, int y) { return root(x) == root(y); }
 
    void clear() {
        cnt = n;
        fill(begin(data), end(data), -1);
    }
};

template <int mod> struct Mod_Int {
    int x;

    Mod_Int() : x(0) {}

    Mod_Int(long long y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

    static int get_mod() { return mod; }

    Mod_Int& operator+=(const Mod_Int& p) {
        if ((x += p.x) >= mod) x -= mod;
        return *this;
    }

    Mod_Int& operator-=(const Mod_Int& p) {
        if ((x += mod - p.x) >= mod) x -= mod;
        return *this;
    }

    Mod_Int& operator*=(const Mod_Int& p) {
        x = (int)(1LL * x * p.x % mod);
        return *this;
    }

    Mod_Int& operator/=(const Mod_Int& p) {
        *this *= p.inverse();
        return *this;
    }

    Mod_Int& operator++() { return *this += Mod_Int(1); }

    Mod_Int operator++(int) {
        Mod_Int tmp = *this;
        ++*this;
        return tmp;
    }

    Mod_Int& operator--() { return *this -= Mod_Int(1); }

    Mod_Int operator--(int) {
        Mod_Int tmp = *this;
        --*this;
        return tmp;
    }

    Mod_Int operator-() const { return Mod_Int(-x); }

    Mod_Int operator+(const Mod_Int& p) const { return Mod_Int(*this) += p; }

    Mod_Int operator-(const Mod_Int& p) const { return Mod_Int(*this) -= p; }

    Mod_Int operator*(const Mod_Int& p) const { return Mod_Int(*this) *= p; }

    Mod_Int operator/(const Mod_Int& p) const { return Mod_Int(*this) /= p; }

    bool operator==(const Mod_Int& p) const { return x == p.x; }

    bool operator!=(const Mod_Int& p) const { return x != p.x; }

    Mod_Int inverse() const {
        assert(*this != Mod_Int(0));
        return pow(mod - 2);
    }

    Mod_Int pow(long long k) const {
        Mod_Int now = *this, ret = 1;
        for (; k > 0; k >>= 1, now *= now) {
            if (k & 1) ret *= now;
        }
        return ret;
    }

    friend ostream& operator<<(ostream& os, const Mod_Int& p) {
        return os << p.x;
    }

    friend istream& operator>>(istream& is, Mod_Int& p) {
        long long a;
        is >> a;
        p = Mod_Int<mod>(a);
        return is;
    }
};

ll mpow2(ll x, ll n, ll mod) {
    ll ans = 1;
    x %= mod;
    while (n != 0) {
        if (n & 1) ans = ans * x % mod;
        x = x * x % mod;
        n = n >> 1;
    }
    ans %= mod;
    return ans;
}

template <typename T> T modinv(T a, const T& m) {
    T b = m, u = 1, v = 0;
    while (b > 0) {
        T t = a / b;
        swap(a -= t * b, b);
        swap(u -= t * v, v);
    }
    return u >= 0 ? u % m : (m - (-u) % m) % m;
}

ll divide_int(ll a, ll b) {
    if (b < 0) a = -a, b = -b;
    return (a >= 0 ? a / b : (a - b + 1) / b);
}

// const int MOD = 1000000007;
const int MOD = 998244353;
using mint = Mod_Int<MOD>;

// ----- library -------
// ----- library -------

int main() {
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    cout << fixed << setprecision(15);

    int n, m;
    cin >> n >> m;
    vector<int> u(m), v(m), c(m);
    rep(i, m) cin >> u[i] >> v[i] >> c[i], u[i]--, v[i]--;
    vector<pii> es;
    rep(i, m) es.eb(c[i], i);
    sort(all(es));
    Union_Find_Tree uf(n);
    vector<int> f(m, 0);
    vector<vector<pii>> g(n);
    for (auto [nc, id] : es) {
        if (uf.unite(u[id], v[id])) {
            f[id] = 1;
            g[u[id]].eb(v[id], id);
            g[v[id]].eb(u[id], id);
        }
    }
    vector<int> d(n, 0), p(n, -1), pe(n, -1);
    auto dfs = [&](int now, auto &&dfs) ->void {
        for (auto [nv, id] : g[now]) {
            if (nv == p[now])
                continue;
            d[nv] = d[now] + 1;
            p[nv] = now;
            pe[nv] = id;
            dfs(nv, dfs);
        }
    };
    dfs(0, dfs);
    auto fl = f;
    rep(i, m) {
        if (f[i])
            continue;
        int nu = u[i], nv = v[i];
        while (nu != nv) {
            if (d[nu] < d[nv])
                swap(nu, nv);
            if (c[i] == c[pe[nu]])
                fl[pe[nu]] = 0;
            nu = p[nu];
        }
    }
    int num = 0, sum = 0;
    rep(i, m) if (fl[i]) num++, sum += c[i];
    cout << num << ' ' << sum << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 4368
final_rank: 2

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

#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
void output(modint1000000007 x){cout << x.val();}
void output(modint998244353 x){cout << x.val();}
#endif

using ll = long long;
using ull = unsigned long long;
using vi = vector<int>;
using vll = vector<long long>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using vvll = vector<vll>;
using vvvll = vector<vvll>;
using vs = vector<string>;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using vcc = vector<char>;
using vvcc = vector<vcc>;
using mii = map<int,int>;
using mll = map<ll,ll>;
template<typename T> using vc = vector<T>;
template<typename T> using vvc = vector<vector<T>>;
template<typename T,typename Y> using uomap = unordered_map<T,Y>;
template<typename T> using uoset = unordered_set<T>;
template<typename T> using revpriority_queue = priority_queue<T,vector<T>,greater<T>>;

int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };
int ddx[8] = { -1,0,1,1,1,0,-1,-1 };
int ddy[8] = { 1,1,1,0,-1,-1,-1,0 };
const long double lpi = 3.141592653589793238;
const double pi = 3.141592653589793;
ll mod = 1000000007;
const ll inf = 1073741824;//2^30
const ll llinf = 1152921504606846976;//2^60
void set_mod(){mod = 998244353;}

#define F first
#define S second
#define pb push_back
#define mp make_pair

template<typename T,typename Y>void input(pair<T,Y>&x){cin >> x.F >> x.S;}
template<typename T>void input(T &x){cin >> x;}
template<typename T>void input(vector<T>&x){for(auto &i:x)input(i);}
template<typename T>void input(vector<vector<T>>&x){for(auto &i:x)input(i);}
template<typename T,typename ...Y>void input(T& x,Y&...k){input(x);input(k...);}
template<typename T,typename Y>void output(pair<T,Y>&x){cout << x.first << ' ' <<  x.second;}
template<typename T>void output(T &x){cout << x;}
template<typename T>void output(vector<T>&x){ll cnt=0;for(auto &i:x){cnt++;output(i);if(cnt!=x.size())cout << ' ';}}
template<typename T>void output(vector<vector<T>>&x){ll cnt=0;for(auto &i:x){cnt++;output(i);if(cnt!=x.size())cout << endl;}}
template<typename T>void print(T &x){output(x);cout << endl;}
template<typename T,typename ...Y>void print(T& x,Y&...k){output(x);cout << endl;print(k...);}
template<typename... T>constexpr auto min(T... a){return min(initializer_list<common_type_t<T...>>{a...});}
template<typename... T>constexpr auto max(T... a){return max(initializer_list<common_type_t<T...>>{a...});}
template<typename T> auto min(const T& a){ return *min_element(all(a)); }
template<typename T> auto max(const T& a){ return *max_element(all(a)); }
template<typename T> inline bool chmin(T &a,T b){if(a>b){a=b;return true;}return false;}
template<typename T> inline bool chmax(T &a,T b){if(a<b){a=b;return true;}return false;}
template<typename T> void yesno(T x){if(x)cout << "Yes" << endl;else cout << "No" << endl;}
#define rep1(end) for(ll i=0;i<(ll)end;i++)
#define rep2(i,end) for(ll i=0;i<(ll)end;i++)
#define rep3(i,start,end) for(ll i=start;i<(ll)end;i++)
#define rep4(i,start,end,plus) for(ll i=start;i<(ll)end;i+=plus)
#define overload4(a,b,c,d,e,...)e
#define rep(...) overload4(__VA_ARGS__,rep4,rep3,rep2,rep1)(__VA_ARGS__)
#define LL(...) ll __VA_ARGS__; input(__VA_ARGS__)
#define rreps(i, a, n) for (ll i = (a); i >= (ll)(n); i--)
#define rrep(i,n) for(ll i=n-1;i>=0;i--)
#define rrepd(i,n) for(ll i=n;i>=1;i--)
#define vrep(i,x) for(auto i:x)
#define vsrep(i,x) for(auto &i:x)
#define all(x) x.begin(),x.end()
#define rall(x) x.rbegin(),x.rend()

//using mint = modint1000000007;
//using mint = modint998244353;

#ifndef ATCODER_DSU_HPP
#define ATCODER_DSU_HPP 1

#include <algorithm>
#include <cassert>
#include <vector>

namespace atcoder {

// Implement (union by size) + (path compression)
// Reference:
// Zvi Galil and Giuseppe F. Italiano,
// Data structures and algorithms for disjoint set union problems
struct dsu {
  public:
    dsu() : _n(0) {}
    dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }

  private:
    int _n;
    // root node: -1 * component size
    // otherwise: parent
    std::vector<int> parent_or_size;
};

}  // namespace atcoder

#endif  // ATCODER_DSU_HPP


struct Edge{
    long long u,v,cost;
};

Edge g={-1,-1,-1};

struct Kruskal{
    atcoder::dsu d;
    long long sum;
    vector<Edge> result;
    long long V;
    vector<vector<pair<long long,long long>>> graph;//First goal Second cost
    vector<Edge> edges;
    Kruskal(const vector<Edge> &edges_,int V_) :
        edges(edges_),V(V_){init();}
    Kruskal(const vector<vector<pair<long long, long long>>> & graph_,int V_) :
        graph(graph_),V(V_){
            for(int i=0;i<V;i++){
                for(int j=0;j<graph[i].size();j++){
                    edges.push_back({i,graph[i][j].F,graph[i][j].S});
                }
            }
            init();
        }
    void init(){
        sort(edges.begin(),edges.end(),[](const Edge &e1,const Edge &e2){
            return e1.cost < e2.cost;
        });
        d = atcoder::dsu(V);
        sum = 0;
        for(auto e:edges){
            if(e.u==g.u&&e.v==g.v&&e.cost==g.cost)continue;
            if(!d.same(e.u,e.v)){
                d.merge(e.u,e.v);
                result.push_back(e);
                sum+=e.cost;
            }
        }
    }
};

/*
Depends on Atcoder Library
*/

int main(){
    //cout << fixed << setprecision(16);
    //ifstream in("input.txt");
    //cin.rdbuf(in.rdbuf());
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    LL(n,m);
    vc<Edge>e;
    rep(i,m){
        LL(s,d,c);
        s--,d--;
        e.pb({s,d,c});
    }
    Kruskal k(e,n);
    ll ans=0,cnt=0;
    vrep(edg,k.result){
        g=edg;
        Kruskal tes(e,n);
        if(tes.sum!=k.sum){
            cnt++;
            ans+=edg.cost;
        }
    }
    cout << cnt << " " << ans << endl;
}
accuracy: 1
time_ms: 1010
memory_kb: 6792
final_rank: 18

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

#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
void output(modint1000000007 x){cout << x.val();}
void output(modint998244353 x){cout << x.val();}
#endif

using ll = long long;
using ull = unsigned long long;
using vi = vector<int>;
using vll = vector<long long>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using vvll = vector<vll>;
using vvvll = vector<vvll>;
using vs = vector<string>;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using vcc = vector<char>;
using vvcc = vector<vcc>;
using mii = map<int,int>;
using mll = map<ll,ll>;
template<typename T> using vc = vector<T>;
template<typename T> using vvc = vector<vector<T>>;
template<typename T,typename Y> using uomap = unordered_map<T,Y>;
template<typename T> using uoset = unordered_set<T>;
template<typename T> using revpriority_queue = priority_queue<T,vector<T>,greater<T>>;

int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };
int ddx[8] = { -1,0,1,1,1,0,-1,-1 };
int ddy[8] = { 1,1,1,0,-1,-1,-1,0 };
const long double lpi = 3.141592653589793238;
const double pi = 3.141592653589793;
ll mod = 1000000007;
const ll inf = 1073741824;//2^30
const ll llinf = 1152921504606846976;//2^60
void set_mod(){mod = 998244353;}

#define F first
#define S second
#define pb push_back
#define mp make_pair

template<typename T,typename Y>void input(pair<T,Y>&x){cin >> x.F >> x.S;}
template<typename T>void input(T &x){cin >> x;}
template<typename T>void input(vector<T>&x){for(auto &i:x)input(i);}
template<typename T>void input(vector<vector<T>>&x){for(auto &i:x)input(i);}
template<typename T,typename ...Y>void input(T& x,Y&...k){input(x);input(k...);}
template<typename T,typename Y>void output(pair<T,Y>&x){cout << x.first << ' ' <<  x.second;}
template<typename T>void output(T &x){cout << x;}
template<typename T>void output(vector<T>&x){ll cnt=0;for(auto &i:x){cnt++;output(i);if(cnt!=x.size())cout << ' ';}}
template<typename T>void output(vector<vector<T>>&x){ll cnt=0;for(auto &i:x){cnt++;output(i);if(cnt!=x.size())cout << endl;}}
template<typename T>void print(T &x){output(x);cout << endl;}
template<typename T,typename ...Y>void print(T& x,Y&...k){output(x);cout << endl;print(k...);}
template<typename... T>constexpr auto min(T... a){return min(initializer_list<common_type_t<T...>>{a...});}
template<typename... T>constexpr auto max(T... a){return max(initializer_list<common_type_t<T...>>{a...});}
template<typename T> auto min(const T& a){ return *min_element(all(a)); }
template<typename T> auto max(const T& a){ return *max_element(all(a)); }
template<typename T> inline bool chmin(T &a,T b){if(a>b){a=b;return true;}return false;}
template<typename T> inline bool chmax(T &a,T b){if(a<b){a=b;return true;}return false;}
template<typename T> void yesno(T x){if(x)cout << "Yes" << endl;else cout << "No" << endl;}
#define rep1(end) for(ll i=0;i<(ll)end;i++)
#define rep2(i,end) for(ll i=0;i<(ll)end;i++)
#define rep3(i,start,end) for(ll i=start;i<(ll)end;i++)
#define rep4(i,start,end,plus) for(ll i=start;i<(ll)end;i+=plus)
#define overload4(a,b,c,d,e,...)e
#define rep(...) overload4(__VA_ARGS__,rep4,rep3,rep2,rep1)(__VA_ARGS__)
#define LL(...) ll __VA_ARGS__; input(__VA_ARGS__)
#define rreps(i, a, n) for (ll i = (a); i >= (ll)(n); i--)
#define rrep(i,n) for(ll i=n-1;i>=0;i--)
#define rrepd(i,n) for(ll i=n;i>=1;i--)
#define vrep(i,x) for(auto i:x)
#define vsrep(i,x) for(auto &i:x)
#define all(x) x.begin(),x.end()
#define rall(x) x.rbegin(),x.rend()

//using mint = modint1000000007;
//using mint = modint998244353;

#ifndef ATCODER_DSU_HPP
#define ATCODER_DSU_HPP 1

#include <algorithm>
#include <cassert>
#include <vector>

namespace atcoder {

// Implement (union by size) + (path compression)
// Reference:
// Zvi Galil and Giuseppe F. Italiano,
// Data structures and algorithms for disjoint set union problems
struct dsu {
  public:
    dsu() : _n(0) {}
    dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }

  private:
    int _n;
    // root node: -1 * component size
    // otherwise: parent
    std::vector<int> parent_or_size;
};

}  // namespace atcoder

#endif  // ATCODER_DSU_HPP

using namespace atcoder;

struct Edge{
    long long u,v,cost;
};

Edge g={-1,-1,-1};

struct Kruskal{
    dsu d;
    long long sum;
    vector<Edge> result;
    long long V;
    vector<vector<pair<long long,long long>>> graph;//First goal Second cost
    vector<Edge> edges;
    Kruskal(const vector<Edge> &edges_,int V_) :
        edges(edges_),V(V_){init();}
    Kruskal(const vector<vector<pair<long long, long long>>> & graph_,int V_) :
        graph(graph_),V(V_){
            for(int i=0;i<V;i++){
                for(int j=0;j<graph[i].size();j++){
                    edges.push_back({i,graph[i][j].F,graph[i][j].S});
                }
            }
            init();
        }
    void init(){
        sort(edges.begin(),edges.end(),[](const Edge &e1,const Edge &e2){
            return e1.cost < e2.cost;
        });
        d = dsu(V);
        sum = 0;
        for(auto e:edges){
            if(e.u==g.u&&e.v==g.v&&e.cost==g.cost)continue;
            if(!d.same(e.u,e.v)){
                d.merge(e.u,e.v);
                result.push_back(e);
                sum+=e.cost;
            }
        }
    }
};

/*
Depends on Atcoder Library
*/

int main(){
    //cout << fixed << setprecision(16);
    //ifstream in("input.txt");
    //cin.rdbuf(in.rdbuf());
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    LL(n,m);
    vc<Edge>e;
    rep(i,m){
        LL(s,d,c);
        s--,d--;
        e.pb({s,d,c});
    }
    Kruskal k(e,n);
    ll ans=0,cnt=0;
    vrep(edg,k.result){
        g=edg;
        Kruskal tes(e,n);
        if(tes.sum!=k.sum){
            cnt++;
            ans+=edg.cost;
        }
    }
    cout << cnt << " " << ans << endl;
}
accuracy: 1
time_ms: 990
memory_kb: 6792
final_rank: 17

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <utility>

using namespace std;
using ll = long long;

// static const int INF = 1<< 29;
// static const ll INF = 1LL << 60;

template <class T> bool chmin(T& a, const T& b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}

using Edge = pair<ll, pair<int, int>>;


struct UnionFind {
    vector<int> par, rank;

    UnionFind(int n): par(n), rank(n, 0) {
        for (int i{0}; i < n; i++) {
            par[i] = i;
        }
    }

    int root(int x) {
        if (par[x] == x) {
            return x;
        }
        return par[x] = root(par[x]);
    }

    bool issame(int x, int y) {
        return root(x) == root(y);
    }

    bool unite(int x, int y) {
        x = root(x);
        y = root(y);

        if (x == y) {
            return false;
        }

        if (rank[y] > rank[x]) {
            swap(x, y);
        }
        if (rank[x] == rank[y]) {
            rank[x]++;
        }
        par[y] = x;
        return true;
    }
};

int main() {
    int V, E;
    cin >> V >> E;
    int s, t;
    ll w;
    vector<Edge> edges(E);
    for (int i{0}; i < E; i++) {
        cin >> s >> t >> w;
        s--; t--;
        edges[i] = Edge(w, make_pair(s, t));
    }
    sort(edges.begin(), edges.end());
    vector<int> candidate_edge_id_list;

    auto calc = [&](int ex_id) -> ll
    {
        ll res{0};
        UnionFind uf(V);
        if (ex_id == -1) {
            candidate_edge_id_list.clear();
        }
        for (int i{0}; i < E; i++)
        {
            if (i == ex_id) continue;
            s = edges[i].second.first;
            t = edges[i].second.second;
            w = edges[i].first;
            if (!uf.issame(s, t))
            {
                uf.unite(s, t);
                res += w;
                if (ex_id == -1) {
                    candidate_edge_id_list.push_back(i);
                }
            }
        }
        return res;
    };

    ll base = calc(-1);
    int num{0};
    ll sum_cost{0};

    for (int ex_id : candidate_edge_id_list)
    {
        ll other = calc(ex_id);
        if (base != other) {
            num++;
            sum_cost += edges[ex_id].first;
        }
    }
    cout << num << " " << sum_cost << endl;
    return 0;
}
accuracy: 1
time_ms: 90
memory_kb: 3840
final_rank: 7

code_snippet: #include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <string>
#include <set>
#include <map>
#include <algorithm>
#include <numeric>
#include <queue>
#include <cassert>
#include <cmath>
#include <bitset>
#include <cctype>
using namespace std;

class Edge {
public:
  int from, to;
  long long cost;
  int id;

  Edge() {}
  Edge(int f, int t, long long c, int i) : from(f), to(t), cost(c), id(i) {}

  bool operator<(const Edge& right) const {
    return cost < right.cost;
  }
};
class UnionFind {
private:
  vector<int> par;

public:
  UnionFind(int N) {
    par.resize(N);
    iota(par.begin(), par.end(), 0);
  }

  int root(int x) {
    return par[x] == x ? x : par[x] = root(par[x]);
  }

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

  void unite(int x, int y) {
    if (same(x, y)) return;
    x = root(x);
    y = root(y);
    par[y] = x;
  }
};

class MinimumSpanningTree {
private:
  vector<Edge> original;
  vector<int> treeEdges;
  long long cost;

public:
  MinimumSpanningTree(int n, vector<Edge> e, int skip = -1) : cost(0), original(e) {
    sort(original.begin(), original.end());
    
    UnionFind uf(n);
    for (int i = 0; i < original.size(); i++) {
      if (original[i].id == skip) continue;

      if (uf.same(original[i].from, original[i].to)) continue;

      treeEdges.push_back(original[i].id);
      //printf("treeEdges.push_back(%ld)\n", original[i].id);
      uf.unite(original[i].from, original[i].to);
      cost += original[i].cost;
    }
  }

  vector<int> getTreeEdges() {
    return treeEdges;
  }

  long long getCost() {
    return cost;
  }

};


int main() {
  int n, m;
  cin >> n >> m;
  vector<Edge> e(m);
  for (int i = 0; i < m; i++) {
    cin >> e[i].from >> e[i].to >> e[i].cost;
    e[i].from--;
    e[i].to--;
    e[i].id = i;
  }
  MinimumSpanningTree tree(n, e);
  //printf("tree.cost = %lld\n", tree.getCost());
  int cnt = 0;
  long long sum = 0;
  for (int i = 0; i < tree.getTreeEdges().size(); i++) {
    int idx = tree.getTreeEdges()[i];
    //printf("skip edge is ... id : %d, from : %d, to : %d, cost : %lld\n", e[idx].id, e[idx].from, e[idx].to, e[idx].cost);
    MinimumSpanningTree tree2(n, e, idx);
    if (tree.getCost() != tree2.getCost()) {
      cnt++;
      sum += e[idx].cost;
    }
  }
  cout << cnt << " " << sum << endl;
}
accuracy: 1
time_ms: 1210
memory_kb: 7784
final_rank: 19

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <queue>

using namespace std;
using ll = long long;

using Edge = pair<ll, pair<int, int>>;
// static const int INF = 1<< 29;
// static const ll INF = 1LL << 60;

struct UnionFind {
    vector<int> par, rank;

    UnionFind(int n): par(n), rank(n, 0) {
        for (int i{0}; i < n; i++) {
            par[i] = i;
        }
    }
    
    int root(int x) {
        if (par[x] == x) return x;
        return par[x] = root(par[x]);
    }

    bool issame(int x, int y) {
        return root(x) == root(y);
    }

    bool unite(int x, int y) {
        x = root(x);
        y = root(y);

        if (x == y) {
            return false;
        }
        if (rank[x] < rank[y]) {
            swap(x, y);
        }
        if (rank[x] == rank[y]) {
            rank[x]++;
        }
        par[y] = x;
        return true;
    }
};

int main() {
    int V, E;
    cin >> V >> E;

    vector<Edge> edges(E);
    int u, v, w;
    for (int i{0}; i < E; i++) {
        cin >> u >> v >> w;
        u--; v--;
        edges[i] = Edge(w, make_pair(u, v));
    }
    sort(edges.begin(), edges.end());
    vector<int> extracted_spanning_tree_edges;

    auto calc = [&](int id) -> ll
    {
        UnionFind uf(V);
        ll res{0};
        if (id == -1) extracted_spanning_tree_edges.clear();
        for (int i{0}; i < E; i++)
        {
            if (i == id) continue;
            Edge e = edges[i];
            int s = e.second.first;
            int t = e.second.second;
            ll w = e.first;
            if (uf.issame(s, t))
                continue;
            uf.unite(s, t);
            res += w;
            if (id == -1) {
                extracted_spanning_tree_edges.push_back(i);
            }
        }
        return res;
    };
    
    ll base = calc(-1);

    int num{0};
    ll cost_sum{0};
    for (auto ex_id : extracted_spanning_tree_edges)
    {
        ll candidate = calc(ex_id);
        if (candidate != base) {
            num++;
            cost_sum += edges[ex_id].first;
        }
    }
    cout << num << " " << cost_sum << endl;
    return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 3912
final_rank: 9

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// Union-Find
struct UnionFind {
    vector<int> par;

    UnionFind() { }
    UnionFind(int n) : par(n, -1) { }
    void init(int n) { par.assign(n, -1); }
    
    int root(int x) {
        if (par[x] < 0) return x;
        else return par[x] = root(par[x]);
    }
    
    bool issame(int x, int y) {
        return root(x) == root(y);
    }
    
    bool merge(int x, int y) {
        x = root(x); y = root(y);
        if (x == y) return false;
        if (par[x] > par[y]) swap(x, y); // merge technique
        par[x] += par[y];
        par[y] = x;
        return true;
    }
    
    int size(int x) {
        return -par[root(x)];
    }
};

// 辺を表す構造体
using pint = pair<int, int>;  // 両端点
using Edge = pair<long long, pint>;  // (重み, 両端点)

int main() {
    // 入力
    int N, M;
    cin >> N >> M;
    vector<Edge> edges(M);
    for (int i = 0; i < M; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        --u, --v;
        edges[i] = Edge(w, pint(u, v));
    }

    // 各辺を重みの小さい順にソート
    sort(edges.begin(), edges.end());

    // id 番目の辺を削除した場合の最小全域木を求める関数
    // id == -1 のときは、通常の最小全域木を求める
    // res は、最小全域木をなす辺番号が格納される
    auto calc = [&](vector<int> &res, int id = -1) -> long long {
        long long cost = 0;
        res.clear();

        // Kruskal 法
        UnionFind uf(N);
        for (int i = 0; i < edges.size(); ++i) {
            if (i == id) continue;
            int u = edges[i].second.first, v = edges[i].second.second;
            if (uf.issame(u, v)) continue;
            res.push_back(i);
            cost += edges[i].first;
            uf.merge(u, v);
        }

        // N-1 本に満たない場合は連結でない
        if (res.size() < N - 1) return (1LL<<60);
        return cost;
    };
    
    // もとのグラフの最小全域木を求める
    vector<int> mst;
    long long base = calc(mst);

    // 最小全域木 mst に含まれる各辺 e を 1 本ずつ除去して調べる
    vector<int> dammy;
    long long num = 0, res = 0;
    for (auto id : mst) {
        if (calc(dammy, id) > base) {
            ++num;
            res += edges[id].first;
        }
    }
    cout << num << " " << res << endl;
}
accuracy: 1
time_ms: 110
memory_kb: 3912
final_rank: 11

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <queue>

using namespace std;
using ll = long long;

// static const int INF = 1 << 29;
// static const ll INF = 1 << 60;

struct UnionFind {
    vector<int> par, rank;

    UnionFind(int n): par(n), rank(n, 0) {
        for (int i{0}; i < n; i++) {
            par[i] = i;
        }
    }

    int root(int x) {
        if (par[x] == x) {
            return x;
        }
        return par[x] = root(par[x]);
    }

    bool issame(int x, int y) {
        return root(x) == root(y);
    }

    bool unite(int x, int y) {
        x = root(x);
        y = root(y);
        if (x == y) {
            return false;
        }
        if (rank[x] < rank[y]) {
            swap(x, y);
        }
        par[y] = x;
        if (rank[x] == rank[y]) {
            rank[x]++;
        }
        return true;
    }
};

void printVec(const vector<int> &a) {
    int N{(int)a.size()};
    for (int i{0}; i < N; i++) {
        if (i > 0) {
            cout << " ";
        }
        cout << a[i];
    }
    cout << endl;
}


int main()
{
    int V, E;
    cin >> V >> E;
    int s, t, w;
    vector<pair<int, pair<int, int>>> edges(E);
    for (int i{0}; i < E; i++) {
        cin >> s >> t >> w;
        edges[i] = {w, make_pair(s - 1, t -1)};
    }
    sort(edges.begin(), edges.end());
    vector<int> search_edge;

    auto calc = [&](int id) -> int
    {
        int sum{0};
        UnionFind uf(V);
        for (int i{0}; i < E; i++)
        {
            if (i == id)
                continue;
            s = edges[i].second.first;
            t = edges[i].second.second;
            w = edges[i].first;
            if (uf.issame(s, t))
            {
                continue;
            }
            uf.unite(s, t);
            sum += w;
            if (id == -1) {
                search_edge.push_back(i);
            }
        }
        return sum;
    };

    int base = calc(-1);
    int cnt{0}, sum_of_construction{0};

    for (auto i : search_edge)
    {
        int candidate_sum = calc(i);
        if (base != candidate_sum) {
            cnt++;
            sum_of_construction += edges[i].first;
        }
    }
    cout << cnt << " " << sum_of_construction << endl;

    return 0;
}
accuracy: 1
time_ms: 90
memory_kb: 3644
final_rank: 4

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <queue>

using namespace std;
using ll = long long;

const ll INF = 1LL << 60;

using Edge = pair<ll, pair<int, int>>;


struct UnionFInd {
    vector<int> par, rank;

    UnionFInd(int n): par(n), rank(n, 0) {
        for (int i{0}; i < n; i++) {
            par[i] = i;
        }
    }

    int root(int x) {
        if (par[x] == x) {
            return x;
        }
        return par[x] = root(par[x]);
    }

    bool issame(int x, int y) {
        return root(x) == root(y);
    }

    bool unite(int x, int y) {
        x = root(x);
        y = root(y);

        if (x == y) {
            return false;
        }
        if (rank[x] < rank[y]) {
            swap(x, y);
        }
        par[y] = x;
        if (rank[x] == rank[y]) {
            rank[x]++;
        }
        return true;
    }
};


int main()
{
    int V, E;
    cin >> V >> E;
    int s, t;
    ll w;

    vector<Edge> edges(E);

    for(int i{0}; i < E; i++) {
        cin >> s >> t >> w;
        edges[i] = Edge(w, make_pair(s - 1, t - 1));
    }

    sort(edges.begin(), edges.end());

    auto calc = [&](vector<int> &res, int id = -1) -> ll {
        ll cost{0};
        res.clear();

        UnionFInd uf(V);
        for (int i{0}; i < edges.size(); i++) {
            if (i == id) continue;
            int u = edges[i].second.first;
            int v = edges[i].second.second;
            if (uf.issame(u, v)) continue;
            res.push_back(i);
            cost += edges[i].first;
            uf.unite(u, v);
        }

        if (res.size() < V - 1) return INF;
        return cost;
    };

    vector<int> mst;
    ll base = calc(mst, -1);

    vector<int> dammy;
    ll num{0}, res{0};
    for (auto id: mst) {
        if (calc(dammy, id) > base) {
            ++num;
            res += edges[id].first;
        }
    }

    cout << num << " " << res << endl;

    return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 3908
final_rank: 8