Problem:
E: 均衡な辺削除 (Balanced Edge Deletion)

問題

N 頂点 M 辺の重み付き単純無向グラフ G が与えられる。
頂点には 1 から N、 辺には 1 から M の番号がつけられている。
i 番目の辺は頂点 u_i と v_i を結んでおり、そのコストは w_i である。

このグラフに対して、以下の操作を 1 度だけ行うことを考える。

G の辺集合から辺を 1 つ選択し、その辺を削除する。

上の操作によってグラフが分割されることがあるので、操作後のグラフを A, B と表記することとする。(分割されない場合、B は空グラフであるとする。)
W(X) をグラフ X 内にある辺のコストの総和 (グラフに辺が存在しない場合 W(X)=0) とし、$\mathrm{cost}$(A,B)=|W(A)−W(B)| と定義する。$\mathrm{cost}$(A,B) が最小になるような辺 (u,v) を求めよ。複数存在する場合は、u が最小であるものを答えよ。それでも複数存在する場合は、v が最小であるものを答えよ。

入力形式

入力は以下の形式で与えられます。

N M
u_1 v_1 w_1
...
u_M v_M w_M

制約

2 \leq N \leq 10^5

1 \leq M \leq 10^5

1 \leq u_i < v_i \leq N

i \neq j ならば u_i \neq u_j または v_i \neq v_j

1 \leq w_i \leq 10^9

与えられるグラフは連結である。

出力形式

答えとなる辺を以下のように空白区切りで一行に出力せよ。

u v

u, v は整数

1 \leq u, v \leq N

入力例1

5 4
1 2 1
2 3 10
3 4 5
4 5 1

出力例1

2 3

入力例2

10 11
1 2 1
2 3 10
1 5 2
3 4 7
3 5 9
5 6 8
6 7 5
6 8 3
7 8 12
7 9 1
9 10 8

出力例2

5 6

Solution in C++14 (judge verdict: Accepted)

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for (int i=0;i<(n);i++)
#define REP2(i,m,n) for (int i=m;i<(n);i++)
typedef long long ll;
typedef long double ld;
#define fst first
#define snd second
#define all(c) ((c).begin()), ((c).end())

struct Graph {
    int n;
    vector<vector<int>> adj;
    Graph(int n) : n(n), adj(n) { }
    void addEdge(int src, int dst) {
        adj[src].push_back(dst);
        adj[dst].push_back(src);
    }
};

struct BridgeBlockTree : Graph {
    vector<int> index;         // index[u] is the block index containing u
    vector<vector<int>> block; // u in block[k] <=> index[u] == k

    BridgeBlockTree(Graph g) : Graph(0) {
        index.assign(g.n, -1);
        vector<int> num(g.n), par(g.n,-1), cur(g.n);

        for (int s = 0; s < g.n; ++s) {
            if (num[s]) continue;
            int time = 0;
            vector<int> snum, path, stack = {s};
            while (!stack.empty()) {
                int u = stack.back();
                if (cur[u] == 0) {
                    num[u] = ++time;
                    path.push_back(u);
                    snum.push_back(num[u]);
                }
                if (cur[u] == g.adj[u].size()) {
                    if (num[u] == snum.back()) {
                        snum.pop_back();
                        block.push_back({});
                        while (1) {
                            int w = path.back(); path.pop_back();
                            block.back().push_back(w);
                            index[w] = block.size()-1;
                            if (u == w) break;
                        }
                    }
                    stack.pop_back();
                } else {
                    int v = g.adj[u][cur[u]++];
                    if (!num[v]) {
                        par[v] = u;
                        stack.push_back(v);
                    } else if (v != par[u] && index[v] < 0) {
                        while (snum.back() > num[v]) snum.pop_back();
                    }
                }
            }
        }
        n = block.size();
        adj.resize(n);
        for (int u = 0; u < g.n; ++u)
            if (par[u] >= 0 && index[u] != index[par[u]])
                addEdge(index[u], index[par[u]]);
    }
};

int N, M, u, v;
ll w;
map<int, ll> W[101010];
map<int, map<int, pair<int,int>>> mp;
ll WS[101010];
ll all_sum = 0;
ll ans = 1LL << 59;
int ans_u;
int ans_v;


ll dfs(int n, int p, BridgeBlockTree &t) {
    ll tmp = WS[n];
    for (auto m: t.adj[n]) {
        if (m != p)
            tmp += dfs(m, n, t);
    }
    if (p == -1) {
        return 0LL;
    }

    ll wp = W[mp[n][p].first][mp[n][p].second];
    ll tmp2 = abs((all_sum - tmp - wp) - tmp);

    if (tmp2 == ans) {
        if (mp[n][p].first < ans_u) {
            ans_u = mp[n][p].first;
            ans_v = mp[n][p].second;
        } else if (mp[n][p].first == ans_u && mp[n][p].second < ans_v) {
            ans_v = mp[n][p].second;
        }
    } else if (tmp2 < ans) {
        ans = tmp2;
        ans_u = mp[n][p].first;
        ans_v = mp[n][p].second;
    }

    return tmp + wp;
}


void solve() {
    cin >> N >> M;
    Graph g(N);

    REP(i, M) {
        cin >> u >> v >> w;
        u--; v--;
        g.addEdge(u, v);
        W[u][v] = w;
        W[v][u] = w;
        all_sum += w;
    }
    BridgeBlockTree t(g);

    for (int i = 0; i < N; ++i)
        for (auto j: g.adj[i])
            if (i < j && t.index[i] == t.index[j]) {
                WS[t.index[i]] += W[i][j];
                ll tmp = all_sum - W[i][j];
                if (tmp < ans) {
                    ans = tmp;
                    ans_u = i;
                    ans_v = j;
                } else if (tmp == ans) {
                    if (i < ans_u) {
                        ans_u = i;
                        ans_v = j;
                    } else if (i == ans_u && j < ans_v) {
                        ans_v = j;
                    }
                }
            }
            else if (i < j) {
                mp[t.index[i]][t.index[j]] = make_pair(i, j);
                mp[t.index[j]][t.index[i]] = make_pair(i, j);
            }

    dfs(0, -1, t);
    cout << ans_u + 1 << " " << ans_v + 1 << endl;
}

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