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

Submitted solutions:

code_snippet: // AOJ #2893 Balanced Edge Deletion
// 2025.5.29

#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; 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]);
	}
}

vector<vector<pair<int,int>>> g;
vector<int> u1, v1;
vector<ll> w1;
vector<int> ord, low, vis;
int tcnt;
vector<char> is_bridge;

void dfs1(int u, int pe) {
    vis[u] = 1;
    ord[u] = low[u] = tcnt++;
    for (auto &pr : g[u]) {
        int v = pr.first, ei = pr.second;
        if (ei == pe) continue;
        if (!vis[v]) {
            dfs1(v, ei);
            low[u] = min(low[u], low[v]);
            if (low[v] > ord[u]) is_bridge[ei] = 1;
        } else low[u] = min(low[u], ord[v]);
    }
}

vector<int> comp;
void dfs2(int u, int cid) {
    comp[u] = cid;
    for (auto &pr : g[u]) {
        int v = pr.first, ei = pr.second;
        if (comp[v] == -1 && !is_bridge[ei]) dfs2(v, cid);
    }
}

vector<vector<pair<int,ll>>> tg;
vector<ll> csum, nsum, esum;

void dfs3(int u, int p) {
    nsum[u] = csum[u];
    esum[u] = 0;
    for (auto &pr : tg[u]) {
        int v = pr.first;
        ll w = pr.second;
        if (v == p) continue;
        dfs3(v, u);
        nsum[u] += nsum[v];
        esum[u] += esum[v] + w;
    }
}

int main() {
    int n = Cin(), m = Cin();
    g.assign(n, {});
    u1.resize(m);
    v1.resize(m);
    w1.resize(m);
    for (int i = 0; i < m; i++) {
        u1[i] = Cin()-1, v1[i] = Cin()-1, w1[i] = Cin();
        g[u1[i]].emplace_back(v1[i], i);
        g[v1[i]].emplace_back(u1[i], i);
    }

    ord.assign(n, 0);
    low.assign(n, 0);
    vis.assign(n, 0);
    is_bridge.assign(m, 0);
    tcnt = 0;
    dfs1(0, -1);

    comp.assign(n, -1);
    int cc = 0;
    for (int i = 0; i < n; i++) if (comp[i] == -1) dfs2(i, cc++);

    tg.assign(cc, {});
    csum.assign(cc, 0);
    ll totalBridge = 0;
    for (int i = 0; i < m; i++) {
        int a = comp[u1[i]], b = comp[v1[i]];
        if (a == b) csum[a] += w1[i];
        else {
            tg[a].emplace_back(b, w1[i]);
            tg[b].emplace_back(a, w1[i]);
            totalBridge += w1[i];
        }
    }

    ll totalNode = 0;
    for (ll x : csum) totalNode += x;
    ll totalSum = totalNode + totalBridge;

    nsum.assign(cc, 0);
    esum.assign(cc, 0);
    dfs3(0, -1);

    vector<int> par(cc, -1);
    vector<ll> pw(cc, 0);
    function<void(int,int)> setPar = [&](int u, int p) {
        for (auto &pr : tg[u]) {
            int v = pr.first;
            ll w = pr.second;
            if (v == p) continue;
            par[v] = u;
            pw[v] = w;
            setPar(v, u);
        }
    };
    setPar(0, -1);

    ll best = LLONG_MAX;
    pair<int,int> ans = {n, n};

    for (int i = 0; i < m; i++) if (is_bridge[i]) {
        int a = comp[u1[i]], b = comp[v1[i]];
        int child = -1;
        if (par[b] == a) child = b;
        else if (par[a] == b) child = a;
        if (child == -1) continue;
        ll sa = nsum[child] + esum[child];
        ll sb = totalSum - sa - w1[i];
        ll c = llabs(sa - sb);
        int uu = u1[i] + 1, vv = v1[i] + 1;
        if (c < best || (c == best && make_pair(uu, vv) < ans)) {
            best = c;
            ans = {uu, vv};
        }
    }
    for (int i = 0; i < m; i++) if (!is_bridge[i]) {
        ll c = totalSum - w1[i];
        int uu = u1[i] + 1, vv = v1[i] + 1;
        if (c < best || (c == best && make_pair(uu, vv) < ans)) {
            best = c;
            ans = {uu, vv};
        }
    }
    Cout(ans.first), pc(' '), Cout(ans.second), pc('\n');
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 23388
final_rank: 2

code_snippet: #include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
#include <tuple>

using namespace std;

struct edge {
  int v, w;
};

const int N = 1e5;
vector<edge> g[N];
int low[N], ord[N], k;
long long ws[N];
long long total;
tuple<long long, int, int> ans;

void dfs(int u, int p) {
  low[u] = ord[u] = k++;
  for (edge e : g[u]) {
    if (ord[e.v] == -1) {
      dfs(e.v, u);
      ws[u] += e.w;
      ws[u] += ws[e.v];
      low[u] = min(low[u], low[e.v]);
      if (ord[u] < low[e.v]) {
        long long A = ws[e.v];
        long long B = total - ws[e.v] - e.w;
        ans = min(ans, make_tuple(abs(A - B), u, e.v));
        ans = min(ans, make_tuple(abs(A - B), e.v, u));
      } else {
        ans = min(ans, make_tuple(total - e.w, u, e.v));
        ans = min(ans, make_tuple(total - e.w, e.v, u));
      }
    } else if (e.v != p && ord[u] > ord[e.v]) {
      ws[u] += e.w;
      low[u] = min(low[u], ord[e.v]);
      ans = min(ans, make_tuple(total - e.w, u, e.v));
      ans = min(ans, make_tuple(total - e.w, e.v, u));
    }
  }
}

int main() {
  int n, m;
  scanf("%d %d", &n, &m);
  for (int i = 0; i < m; i++) {
    int u, v, w;
    scanf("%d %d %d", &u, &v, &w);
    u--;
    v--;
    g[u].push_back((edge){v, w});
    g[v].push_back((edge){u, w});
    total += w;
  }
  memset(ord, -1, sizeof(ord));
  ans = make_tuple(total, 0, 1);
  dfs(0, -1);
  printf("%d %d\n", get<1>(ans) + 1, get<2>(ans) + 1);
}
accuracy: 1
time_ms: 20
memory_kb: 12072
final_rank: 1

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

long long N;
long long M, A[1 << 19], B[1 << 19], C[1 << 19];
long long Ord[1 << 19], Cnt;
long long Low[1 << 19];
long long Col[1 << 19], Colcnt;
long long Sum1[1 << 19];
long long Sum2[1 << 19];
vector<int> G[1 << 19];
vector<int> H[1 << 19];
vector<int> I[1 << 19];
map<pair<int, int>, int> Bridge;
map<pair<int, int>, int> Cost;

void dfs1(int pos, int par) {
    Cnt += 1;
    Ord[pos] = Cnt;
    Low[pos] = Cnt;
    for (int to : G[pos]) {
        if (to == par) continue;
        if (Ord[to] == 0) {
            dfs1(to, pos);
            Low[pos] = min(Low[pos], Low[to]);
            if (Ord[pos] < Low[to]) {
                Bridge[make_pair(pos, to)] = 1;
                Bridge[make_pair(to, pos)] = 1;
            }
        }
        else {
            Low[pos] = min(Low[pos], Ord[to]);
        }
    }
}

void dfs2(int pos) {
    Col[pos] = Colcnt;
    for (int to : H[pos]) {
        if (Col[to] != 0) continue;
        dfs2(to);
    }
}

void dfs3(int pos, int par) {
    for (int to : I[pos]) {
        if (to == par) continue;
        dfs3(to, pos);
        Sum1[to] += Cost[make_pair(pos, to)];
    }
}

void dfs4(int pos, int par) {
    Sum2[pos] = Sum1[pos];
    for (int to : I[pos]) {
        if (to == par) continue;
        dfs4(to, pos);
        Sum2[pos] += Sum2[to];
    }
}

int main() {
    // Step 1. Input
    scanf("%lld%lld", &N, &M);
    for (int i = 1; i <= M; i++) scanf("%lld%lld%lld", &A[i], &B[i], &C[i]);
    for (int i = 1; i <= M; i++) G[A[i]].push_back(B[i]);
    for (int i = 1; i <= M; i++) G[B[i]].push_back(A[i]);

    // Step 2. Get Bridge
    dfs1(1, -1);

    // Step 3. Make Tree 1
    for (int i = 1; i <= M; i++) {
        if (Bridge[make_pair(A[i], B[i])] == 1) continue;
        H[A[i]].push_back(B[i]);
        H[B[i]].push_back(A[i]);
    }
    for (int i = 1; i <= N; i++) {
        if (Col[i] != 0) continue;
        Colcnt += 1;
        dfs2(i);
    }

    // Step 4. Make Tree 2
    for (int i = 1; i <= M; i++) {
        if (Bridge[make_pair(A[i], B[i])] == 0) { Sum1[Col[A[i]]] += C[i];continue; }
        int idx1 = Col[A[i]];
        int idx2 = Col[B[i]];
        I[idx1].push_back(idx2);
        I[idx2].push_back(idx1);
        Cost[make_pair(idx1, idx2)] = C[i];
        Cost[make_pair(idx2, idx1)] = C[i];
    }
    dfs3(1, -1);
    dfs4(1, -1);

    // Step 5. Get Answer
    long long Answer = (1LL << 60);
    pair<long long, long long> AnsID = make_pair(-1, -1);
    for (int i = 1; i <= M; i++) {
        long long Score = Sum2[1] - C[i];
        if (Bridge[make_pair(A[i], B[i])] == 1) {
            long long u1 = Sum2[Col[A[i]]];
            long long u2 = Sum2[Col[B[i]]]; if (u1 < u2) swap(u1, u2);
            long long parity1 = Sum2[1] - u2;
            long long parity2 = u2 - Cost[make_pair(Col[A[i]], Col[B[i]])];
            Score = abs(parity1 - parity2);
        }
        if (Answer > Score) {
            Answer = Score;
            AnsID = make_pair(100000000, -1);
        }
        if (Answer == Score && AnsID > make_pair(A[i], B[i])) {
            AnsID = make_pair(A[i], B[i]);
        }
    }

    // Step 6. Output
    cout << AnsID.first << " " << AnsID.second << endl;
    return 0;
}
accuracy: 1
time_ms: 220
memory_kb: 88220
final_rank: 13

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int mx = 1e5 + 10;
int cmp[mx],low[mx],ord[mx],vis[mx];
pair<pair<int,int>,ll> ee[mx];
int n,m;
ll all;
vector<int>g[mx];
set<pair<ll,pair<int,int> > > ans;

void dfs(int ni,int p,int&time){
    ord[ni] = low[ni] = time++;
    vis[ni] = 1;
    for(auto&to:g[ni])if(to!=p){
        if(vis[to]){
            low[ni] = min(low[ni],ord[to]);
        }else{
            dfs(to,ni,time);
            low[ni] = min(low[ni],low[to]);
        }
    }
}

void dfs1(int ni,int p,int now){
    vis[ni] = 1;
    cmp[ni] = now;
    for(auto&to:g[ni]){
        if(ord[ni]<low[to]||ord[to]<low[ni]) continue;
        if(vis[to]) continue;
        dfs1(to,ni,now);
    }
}
vector<pair<int,int> >gg[mx];
ll aa[mx],cur[mx];

ll dfs2(int ni,int p){
    ll now = cur[ni];
    for(auto itr:gg[ni]){
        int to;ll w;
        to = itr.first;
        if(to==p) continue;
        w = ee[itr.second].second;
        ll nxt = dfs2(to,ni);
        ll nn = all - nxt - w;
        assert(nn>=0);
        aa[itr.second] = abs(nn-nxt);
        now += nxt + w;
    }
    return now;
}
        
int main(){
    cin>>n>>m;
    for(int i = 0;i<m;i++){
        int u,v;ll w;
        cin>>u>>v>>w;
        u--;v--;
        all+=w;
        ee[i] = make_pair(make_pair(u,v),w);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    int time = 0;
    dfs(0,-1,time);
    int now = 0;
    for(int i = 0;i<n;i++) vis[i] = 0;
    for(int i = 0;i<n;i++)if(vis[i]==0){
        dfs1(i,-1,now++);
    }
    for(int i = 0;i<m;i++){
        int u,v;ll w;
        tie(u,v) = ee[i].first;
        w = ee[i].second;
        u = cmp[u];v = cmp[v];
        if(u==v){
            cur[u] += w;
            aa[i] = -1;
            ans.insert(make_pair(all-w,ee[i].first));
        }else{
            gg[u].push_back(make_pair(v,i));
            gg[v].push_back(make_pair(u,i));
        }
    }
    dfs2(0,-1);
    for(int i = 0;i<m;i++){
        if(aa[i]==-1) continue;
        ans.insert(make_pair(aa[i],ee[i].first));
    }
    auto itr = *ans.begin();
    int u,v;
    tie(u,v) = itr.second;
    cout<<u+1<<" " <<v+1<<endl;
}
accuracy: 1
time_ms: 90
memory_kb: 25164
final_rank: 9

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int mx = 1e6 + 10;
int cmp[mx],low[mx],ord[mx],vis[mx];
pair<pair<int,int>,ll> ee[mx];
int n,m;
ll all;
vector<int>g[mx];
set<pair<ll,pair<int,int> > > ans;

void dfs(int ni,int p,int&time){
    ord[ni] = low[ni] = time++;
    vis[ni] = 1;
    for(auto&to:g[ni])if(to!=p){
        if(vis[to]){
            low[ni] = min(low[ni],ord[to]);
        }else{
            dfs(to,ni,time);
            low[ni] = min(low[ni],ord[ni]);
        }
    }
}

void dfs1(int ni,int p,int now){
    vis[ni] = 1;
    cmp[ni] = now;
    for(auto&to:g[ni]){
        if(ord[ni]<low[to]||ord[to]<low[ni]) continue;
        if(vis[to]) continue;
        dfs1(to,ni,now);
    }
}
vector<pair<int,int> >gg[mx];
ll aa[mx],cur[mx];

ll dfs2(int ni,int p){
    ll now = cur[ni];
    for(auto itr:gg[ni]){
        int to;ll w;
        to = itr.first;
        if(to==p) continue;
        w = ee[itr.second].second;
        ll nxt = dfs2(to,ni);
        ll nn = all - nxt - w;
        assert(nn>=0);
        aa[itr.second] = abs(nn-nxt);
        now += nxt + w;
    }
    return now;
}
        
int main(){
    cin>>n>>m;
    for(int i = 0;i<m;i++){
        int u,v;ll w;
        cin>>u>>v>>w;
        u--;v--;
        all+=w;
        ee[i] = make_pair(make_pair(u,v),w);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    int time = 0;
    dfs(0,-1,time);
    int now = 0;
    for(int i = 0;i<n;i++) vis[i] = 0;
    for(int i = 0;i<n;i++)if(vis[i]==0){
        dfs1(i,-1,now++);
    }
    for(int i = 0;i<m;i++){
        int u,v;ll w;
        tie(u,v) = ee[i].first;
        w = ee[i].second;
        u = cmp[u];v = cmp[v];
        if(u==v){
            cur[u] += w;
            aa[i] = -1;
            ans.insert(make_pair(all-w,ee[i].first));
        }else{
            gg[u].push_back(make_pair(v,i));
            gg[v].push_back(make_pair(u,i));
        }
    }
    dfs2(0,-1);
    for(int i = 0;i<m;i++){
        if(aa[i]==-1) continue;
        ans.insert(make_pair(aa[i],ee[i].first));
    }
    auto itr = *ans.begin();
    int u,v;
    tie(u,v) = itr.second;
    cout<<u+1<<" " <<v+1<<endl;
}
accuracy: 0.35135135135135137
time_ms: 70
memory_kb: 74944
final_rank: 18

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int mx = 1e5 + 10;
int cmp[mx],low[mx],ord[mx],vis[mx];
pair<pair<int,int>,ll> ee[mx];
int n,m;
ll all;
vector<int>g[mx];
set<pair<ll,pair<int,int> > > ans;

void dfs(int ni,int p,int&time){
    ord[ni] = low[ni] = time++;
    vis[ni] = 1;
    for(auto&to:g[ni])if(to!=p){
        if(vis[to]){
            low[ni] = min(low[ni],ord[to]);
        }else{
            dfs(to,ni,time);
            low[ni] = min(low[ni],ord[ni]);
        }
    }
}

void dfs1(int ni,int p,int now){
    vis[ni] = 1;
    cmp[ni] = now;
    for(auto&to:g[ni]){
        if(ord[ni]<low[to]||ord[to]<low[ni]) continue;
        if(vis[to]) continue;
        dfs1(to,ni,now);
    }
}
vector<pair<int,int> >gg[mx];
ll aa[mx],cur[mx];

ll dfs2(int ni,int p){
    ll now = cur[ni];
    for(auto itr:gg[ni]){
        int to;ll w;
        to = itr.first;
        if(to==p) continue;
        w = ee[itr.second].second;
        ll nxt = dfs2(to,ni);
        ll nn = all - nxt - w;
        assert(nn>=0);
        aa[itr.second] = abs(nn-nxt);
        now += nxt + w;
    }
    return now;
}
        
int main(){
    cin>>n>>m;
    for(int i = 0;i<m;i++){
        int u,v;ll w;
        cin>>u>>v>>w;
        u--;v--;
        all+=w;
        ee[i] = make_pair(make_pair(u,v),w);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    int time = 0;
    dfs(0,-1,time);
    int now = 0;
    for(int i = 0;i<n;i++) vis[i] = 0;
    for(int i = 0;i<n;i++)if(vis[i]==0){
        dfs1(i,-1,now++);
    }
    for(int i = 0;i<m;i++){
        int u,v;ll w;
        tie(u,v) = ee[i].first;
        w = ee[i].second;
        u = cmp[u];v = cmp[v];
        if(u==v){
            cur[u] += w;
            aa[i] = -1;
            ans.insert(make_pair(all-w,ee[i].first));
        }else{
            gg[u].push_back(make_pair(v,i));
            gg[v].push_back(make_pair(u,i));
        }
    }
    dfs2(0,-1);
    for(int i = 0;i<m;i++){
        if(aa[i]==-1) continue;
        ans.insert(make_pair(aa[i],ee[i].first));
    }
    auto itr = *ans.begin();
    int u,v;
    tie(u,v) = itr.second;
    cout<<u+1<<" " <<v+1<<endl;
}
accuracy: 0.35135135135135137
time_ms: 70
memory_kb: 18960
final_rank: 17

code_snippet: #include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
using namespace std;
using std::cout;
using std::cin;
using std::endl;
using ll=long long;
using ld=long double;
ll ILL=2167167167167167167;
const int INF=2100000000;
const ll mod=998244353;
#define rep(i,a) for (int i=0;i<a;i++)
#define all(p) p.begin(),p.end()
template<class T> using _pq = priority_queue<T, vector<T>, greater<T>>;
template<class T> ll LB(vector<T> &v,T a){return lower_bound(v.begin(),v.end(),a)-v.begin();}
template<class T> ll UB(vector<T> &v,T a){return upper_bound(v.begin(),v.end(),a)-v.begin();}
template<class T> bool chmin(T &a,const T &b){if(a>b){a=b;return 1;}else return 0;}
template<class T> bool chmax(T &a,const T &b){if(a<b){a=b;return 1;}else return 0;}
template<class T> void So(vector<T> &v) {sort(v.begin(),v.end());}
template<class T> void Sore(vector<T> &v) {sort(v.begin(),v.end(),[](T x,T y){return x>y;});}
void yneos(bool a){if(a) cout<<"Yes\n"; else cout<<"No\n";}
template<class T> void vec_out(vector<T> &p){for(int i=0;i<(int)(p.size());i++){if(i) cout<<" ";cout<<p[i];}cout<<"\n";}
template<class T> T min(vector<T> &a){assert(!a.empty());T ans=a[0];for(auto &x:a) chmin(ans,x);return ans;}
template<class T> T max(vector<T> &a){assert(!a.empty());T ans=a[0];for(auto &x:a) chmax(ans,x);return ans;}

namespace po167{
struct UFtree
{
	int _n;
	std::vector<int> wei;
	std::vector<int> q;
	int component;
	UFtree(int n):_n(n),wei(n),component(n),par(n){
		for(int i=0;i<n;i++){
			wei[i]=1,par[i]=i;
		}
	}
	void intialize(){
		for(auto x:q){
			wei[root(x)]=1;
			par[x]=x;
		}
		component=(int)par.size();
		q={};
	}
	//根っこを返す
	int root(int a){
		assert(0<=a&&a<_n);
		if(a==par[a]) return a;
		return par[a]=root(par[a]);
	}
	//trueなら1,falseなら0
	int same(int a,int b){
		assert(0<=a&&a<_n);
		assert(0<=b&&b<_n);
		if(root(a)==root(b)) return 1;
		else return 0;
	}
	//a,bが違う根っこの元なら結合する,結合したらtrueを返す
	bool unite(int a,int b){
		a=root(a),b=root(b);
		if(a==b) return false;
		if(wei[a]<wei[b]) std::swap(a,b);
		par[b]=a;
		q.push_back(b);
		wei[a]+=wei[b];
		component--;
		return true;
	}
	private:
	std::vector<int> par;
};
}
using po167::UFtree;

namespace po167{
std::vector<std::vector<int>> Lowlink(std::vector<std::vector<int>> &G){
    int n=G.size();
    std::vector<std::vector<int>> p(2,std::vector<int>(n,-1));
    std::vector<int> front(n);
    int k=1;
    std::vector<int> ind(n);
    std::stack<int> s;
    s.push(0);p[0][0]=0,p[1][0]=0;
    while(!s.empty()){
        int a=s.top();
        while(true){
            if(ind[a]==(int)(G[a].size())){
                p[1][front[a]]=std::min(p[1][front[a]],p[1][a]);
                s.pop();
                break;
            }
            int b=G[a][ind[a]];
            ind[a]++;
            if(p[0][b]==-1){
                p[0][b]=k;
                p[1][b]=k;
                front[b]=a;
                k++;
                s.push(b);
                break;
            }
            else if(front[a]!=b){
                if(p[1][a]>p[0][b]) p[1][a]=p[0][b];
            }
        }
    }
    p.push_back(front);
    return p;
}

std::vector<std::pair<int,int>> bridges(std::vector<std::vector<int>> G){
    int n=G.size();
    auto p=Lowlink(G);
    std::vector<std::pair<int,int>> ans;
    for(int i=0;i<n;i++){
        for(int j:G[i]){
            if(p[0][i]<p[0][j]&&p[0][i]<p[1][j]){
                ans.push_back({i,j});
            }
        }
    }
    return ans;
}
//https://onlinejudge.u-aizu.ac.jp/status/users/potato167/submissions/1/GRL_3_A/judge/6651994/C++17
std::vector<int> Articulation_Points(std::vector<std::vector<int>> G){
    int n=G.size();
    auto p=Lowlink(G);
    std::vector<int> ans;
    int tmp=0;
    for(auto x:G[0]){
        if(p[1][x]!=0) tmp++;
    }
    if(tmp>=2) ans.push_back(0);
    for(int i=1;i<n;i++){
        for(int j:G[i]){
            if(i==p[2][j]&&p[0][i]<=p[1][j]){
                ans.push_back(i);
                break;
            }
        }
    }
    return ans;
}
}


void solve();
// oddloop
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t=1;
	//cin>>t;
	rep(i,t) solve();
}

void solve(){
	int N,M;
	cin>>N>>M;
	ll base=0;
	vector<vector<int>> G(N);
	vector<vector<vector<int>>> H(N);
	vector<int> U(M),V(M),C(M);
	pair<int,int> ans={INF,INF};
	ll ans_val=0;
	rep(i,M){
		cin>>U[i]>>V[i]>>C[i];
		U[i]--,V[i]--;
		G[U[i]].push_back(V[i]);
		G[V[i]].push_back(U[i]);
		base+=C[i];
		if(chmax(ans_val,(ll)C[i])){
			ans={U[i],V[i]};
		}else if(ans_val==C[i]){
			chmin(ans,{U[i],V[i]});
		}
	}
	ans_val=base-ans_val;
	auto E=po167::bridges(G);
	set<pair<int,int>> s;
	for(auto x:E) s.insert(x);
	UFtree T(N);
	vector<ll> p(N);
	vector<int> seen(M);
	rep(i,M){
		if(!s.count({U[i],V[i]})&&!s.count({V[i],U[i]})){
			T.unite(U[i],V[i]);
		}else{
			seen[i]=1;
		}
	}
	rep(i,M){
		if(seen[i]==0){
			p[T.root(U[i])]+=C[i];
		}else{
			H[T.root(U[i])].push_back({T.root(V[i]),C[i],U[i],V[i]});
			H[T.root(V[i])].push_back({T.root(U[i]),C[i],V[i],U[i]});
		}
	}
	int root=0;
	while(T.root(root)!=root) root++;
	vector<int> order={root};
	int ind=0;
	vector<int> pare(N,-1);
	pare[root]=-2;
	while(ind!=(int)(order.size())){
		int a=order[ind];
		for(auto x:H[a]){
			if(pare[x[0]]==-1){
				pare[x[0]]=a;
				order.push_back(x[0]);
			}
		}
		ind++;
	}
	for(ind--;ind>0;ind--){
		int a=order[ind];
		ll e=0;
		int n_p=-1,n_q=-1;
		for(auto x:H[a]){
			if(pare[a]==x[0]){
				e=x[1];
				n_p=min(x[3],x[2]);
				n_q=max(x[3],x[2]);
			}
			else{
				p[a]+=p[x[0]]+x[1];
			}
		}
		ll tmp=abs(base-e-p[a]-p[a]);
		pair<int,int> b={n_p,n_q};
		if(chmin(ans_val,tmp)){
			ans=b;
		}else if(ans_val==tmp&&ans>b) ans=b;
	}
	cout<<ans.first+1<<" "<<ans.second+1<<"\n";
}
accuracy: 1
time_ms: 90
memory_kb: 33588
final_rank: 10

code_snippet: #include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
using namespace std;
using std::cout;
using std::cin;
using std::endl;
using ll=long long;
using ld=long double;
ll ILL=2167167167167167167;
const int INF=2100000000;
const ll mod=998244353;
#define rep(i,a) for (int i=0;i<a;i++)
#define all(p) p.begin(),p.end()
template<class T> using _pq = priority_queue<T, vector<T>, greater<T>>;
template<class T> ll LB(vector<T> &v,T a){return lower_bound(v.begin(),v.end(),a)-v.begin();}
template<class T> ll UB(vector<T> &v,T a){return upper_bound(v.begin(),v.end(),a)-v.begin();}
template<class T> bool chmin(T &a,const T &b){if(a>b){a=b;return 1;}else return 0;}
template<class T> bool chmax(T &a,const T &b){if(a<b){a=b;return 1;}else return 0;}
template<class T> void So(vector<T> &v) {sort(v.begin(),v.end());}
template<class T> void Sore(vector<T> &v) {sort(v.begin(),v.end(),[](T x,T y){return x>y;});}
void yneos(bool a){if(a) cout<<"Yes\n"; else cout<<"No\n";}
template<class T> void vec_out(vector<T> &p){for(int i=0;i<(int)(p.size());i++){if(i) cout<<" ";cout<<p[i];}cout<<"\n";}
template<class T> T min(vector<T> &a){assert(!a.empty());T ans=a[0];for(auto &x:a) chmin(ans,x);return ans;}
template<class T> T max(vector<T> &a){assert(!a.empty());T ans=a[0];for(auto &x:a) chmax(ans,x);return ans;}

namespace po167{
struct UFtree
{
	int _n;
	std::vector<int> wei;
	std::vector<int> q;
	int component;
	UFtree(int n):_n(n),wei(n),component(n),par(n){
		for(int i=0;i<n;i++){
			wei[i]=1,par[i]=i;
		}
	}
	void intialize(){
		for(auto x:q){
			wei[root(x)]=1;
			par[x]=x;
		}
		component=(int)par.size();
		q={};
	}
	//根っこを返す
	int root(int a){
		assert(0<=a&&a<_n);
		if(a==par[a]) return a;
		return par[a]=root(par[a]);
	}
	//trueなら1,falseなら0
	int same(int a,int b){
		assert(0<=a&&a<_n);
		assert(0<=b&&b<_n);
		if(root(a)==root(b)) return 1;
		else return 0;
	}
	//a,bが違う根っこの元なら結合する,結合したらtrueを返す
	bool unite(int a,int b){
		a=root(a),b=root(b);
		if(a==b) return false;
		if(wei[a]<wei[b]) std::swap(a,b);
		par[b]=a;
		q.push_back(b);
		wei[a]+=wei[b];
		component--;
		return true;
	}
	private:
	std::vector<int> par;
};
}
using po167::UFtree;

namespace po167{
std::vector<std::vector<int>> Lowlink(std::vector<std::vector<int>> &G){
    int n=G.size();
    std::vector<std::vector<int>> p(2,std::vector<int>(n,-1));
    std::vector<int> front(n);
    int k=1;
    std::vector<int> ind(n);
    std::stack<int> s;
    s.push(0);p[0][0]=0,p[1][0]=0;
    while(!s.empty()){
        int a=s.top();
        while(true){
            if(ind[a]==(int)(G[a].size())){
                p[1][front[a]]=std::min(p[1][front[a]],p[1][a]);
                s.pop();
                break;
            }
            int b=G[a][ind[a]];
            ind[a]++;
            if(p[0][b]==-1){
                p[0][b]=k;
                p[1][b]=k;
                front[b]=a;
                k++;
                s.push(b);
                break;
            }
            else if(front[a]!=b){
                if(p[1][a]>p[0][b]) p[1][a]=p[0][b];
            }
        }
    }
    p.push_back(front);
    return p;
}

std::vector<std::pair<int,int>> bridges(std::vector<std::vector<int>> G){
    int n=G.size();
    auto p=Lowlink(G);
    std::vector<std::pair<int,int>> ans;
    for(int i=0;i<n;i++){
        for(int j:G[i]){
            if(p[0][i]<p[0][j]&&p[0][i]<p[1][j]){
                ans.push_back({i,j});
            }
        }
    }
    return ans;
}
//https://onlinejudge.u-aizu.ac.jp/status/users/potato167/submissions/1/GRL_3_A/judge/6651994/C++17
std::vector<int> Articulation_Points(std::vector<std::vector<int>> G){
    int n=G.size();
    auto p=Lowlink(G);
    std::vector<int> ans;
    int tmp=0;
    for(auto x:G[0]){
        if(p[1][x]!=0) tmp++;
    }
    if(tmp>=2) ans.push_back(0);
    for(int i=1;i<n;i++){
        for(int j:G[i]){
            if(i==p[2][j]&&p[0][i]<=p[1][j]){
                ans.push_back(i);
                break;
            }
        }
    }
    return ans;
}
}


void solve();
// oddloop
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t=1;
	//cin>>t;
	rep(i,t) solve();
}

void solve(){
	int N,M;
	cin>>N>>M;
	ll base=0;
	vector<vector<int>> G(N);
	vector<vector<vector<int>>> H(N);
	vector<int> U(M),V(M),C(M);
	pair<int,int> ans={INF,INF};
	ll ans_val=0;
	rep(i,M){
		cin>>U[i]>>V[i]>>C[i];
		U[i]--,V[i]--;
		G[U[i]].push_back(V[i]);
		G[V[i]].push_back(U[i]);
		base+=C[i];
		if(chmax(ans_val,(ll)C[i])){
			ans={U[i],V[i]};
		}else if(ans_val==C[i]){
			chmin(ans,{U[i],V[i]});
		}
	}
	ans_val=base;
	auto E=po167::bridges(G);
	set<pair<int,int>> s;
	for(auto x:E) s.insert(x);
	UFtree T(N);
	vector<ll> p(N);
	vector<int> seen(M);
	rep(i,M){
		if(!s.count({U[i],V[i]})&&!s.count({V[i],U[i]})){
			T.unite(U[i],V[i]);
		}else{
			seen[i]=1;
		}
	}
	rep(i,M){
		if(seen[i]==0){
			p[T.root(U[i])]+=C[i];
		}else{
			H[T.root(U[i])].push_back({T.root(V[i]),C[i],U[i],V[i]});
			H[T.root(V[i])].push_back({T.root(U[i]),C[i],V[i],U[i]});
		}
	}
	int root=0;
	while(T.root(root)!=root) root++;
	vector<int> order={root};
	int ind=0;
	vector<int> pare(N,-1);
	pare[root]=-2;
	while(ind!=(int)(order.size())){
		int a=order[ind];
		for(auto x:H[a]){
			if(pare[x[0]]==-1){
				pare[x[0]]=a;
				order.push_back(x[0]);
			}
		}
		ind++;
	}
	for(ind--;ind>0;ind--){
		int a=order[ind];
		ll e=0;
		int n_p=-1,n_q=-1;
		for(auto x:H[a]){
			if(pare[a]==x[0]){
				e=x[1];
				n_p=min(x[3],x[2]);
				n_q=max(x[3],x[2]);
			}
			else{
				p[a]+=p[x[0]]+x[1];
			}
		}
		ll tmp=abs(base-e-p[a]-p[a]);
		pair<int,int> b={n_p,n_q};
		if(chmin(ans_val,tmp)){
			ans=b;
		}else if(ans_val==tmp&&ans>b) ans=b;
	}
	cout<<ans.first+1<<" "<<ans.second+1<<"\n";
}
accuracy: 0.32432432432432434
time_ms: 20
memory_kb: 7400
final_rank: 20

code_snippet: #include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
using namespace std;
using std::cout;
using std::cin;
using std::endl;
using ll=long long;
using ld=long double;
ll ILL=2167167167167167167;
const int INF=2100000000;
const ll mod=998244353;
#define rep(i,a) for (int i=0;i<a;i++)
#define all(p) p.begin(),p.end()
template<class T> using _pq = priority_queue<T, vector<T>, greater<T>>;
template<class T> ll LB(vector<T> &v,T a){return lower_bound(v.begin(),v.end(),a)-v.begin();}
template<class T> ll UB(vector<T> &v,T a){return upper_bound(v.begin(),v.end(),a)-v.begin();}
template<class T> bool chmin(T &a,const T &b){if(a>b){a=b;return 1;}else return 0;}
template<class T> bool chmax(T &a,const T &b){if(a<b){a=b;return 1;}else return 0;}
template<class T> void So(vector<T> &v) {sort(v.begin(),v.end());}
template<class T> void Sore(vector<T> &v) {sort(v.begin(),v.end(),[](T x,T y){return x>y;});}
void yneos(bool a){if(a) cout<<"Yes\n"; else cout<<"No\n";}
template<class T> void vec_out(vector<T> &p){for(int i=0;i<(int)(p.size());i++){if(i) cout<<" ";cout<<p[i];}cout<<"\n";}
template<class T> T min(vector<T> &a){assert(!a.empty());T ans=a[0];for(auto &x:a) chmin(ans,x);return ans;}
template<class T> T max(vector<T> &a){assert(!a.empty());T ans=a[0];for(auto &x:a) chmax(ans,x);return ans;}

namespace po167{
struct UFtree
{
	int _n;
	std::vector<int> wei;
	std::vector<int> q;
	int component;
	UFtree(int n):_n(n),wei(n),component(n),par(n){
		for(int i=0;i<n;i++){
			wei[i]=1,par[i]=i;
		}
	}
	void intialize(){
		for(auto x:q){
			wei[root(x)]=1;
			par[x]=x;
		}
		component=(int)par.size();
		q={};
	}
	//根っこを返す
	int root(int a){
		assert(0<=a&&a<_n);
		if(a==par[a]) return a;
		return par[a]=root(par[a]);
	}
	//trueなら1,falseなら0
	int same(int a,int b){
		assert(0<=a&&a<_n);
		assert(0<=b&&b<_n);
		if(root(a)==root(b)) return 1;
		else return 0;
	}
	//a,bが違う根っこの元なら結合する,結合したらtrueを返す
	bool unite(int a,int b){
		a=root(a),b=root(b);
		if(a==b) return false;
		if(wei[a]<wei[b]) std::swap(a,b);
		par[b]=a;
		q.push_back(b);
		wei[a]+=wei[b];
		component--;
		return true;
	}
	private:
	std::vector<int> par;
};
}
using po167::UFtree;

namespace po167{
std::vector<std::vector<int>> Lowlink(std::vector<std::vector<int>> &G){
    int n=G.size();
    std::vector<std::vector<int>> p(2,std::vector<int>(n,-1));
    std::vector<int> front(n);
    int k=1;
    std::vector<int> ind(n);
    std::stack<int> s;
    s.push(0);p[0][0]=0,p[1][0]=0;
    while(!s.empty()){
        int a=s.top();
        while(true){
            if(ind[a]==(int)(G[a].size())){
                p[1][front[a]]=std::min(p[1][front[a]],p[1][a]);
                s.pop();
                break;
            }
            int b=G[a][ind[a]];
            ind[a]++;
            if(p[0][b]==-1){
                p[0][b]=k;
                p[1][b]=k;
                front[b]=a;
                k++;
                s.push(b);
                break;
            }
            else if(front[a]!=b){
                if(p[1][a]>p[0][b]) p[1][a]=p[0][b];
            }
        }
    }
    p.push_back(front);
    return p;
}

std::vector<std::pair<int,int>> bridges(std::vector<std::vector<int>> G){
    int n=G.size();
    auto p=Lowlink(G);
    std::vector<std::pair<int,int>> ans;
    for(int i=0;i<n;i++){
        for(int j:G[i]){
            if(p[0][i]<p[0][j]&&p[0][i]<p[1][j]){
                ans.push_back({i,j});
            }
        }
    }
    return ans;
}
//https://onlinejudge.u-aizu.ac.jp/status/users/potato167/submissions/1/GRL_3_A/judge/6651994/C++17
std::vector<int> Articulation_Points(std::vector<std::vector<int>> G){
    int n=G.size();
    auto p=Lowlink(G);
    std::vector<int> ans;
    int tmp=0;
    for(auto x:G[0]){
        if(p[1][x]!=0) tmp++;
    }
    if(tmp>=2) ans.push_back(0);
    for(int i=1;i<n;i++){
        for(int j:G[i]){
            if(i==p[2][j]&&p[0][i]<=p[1][j]){
                ans.push_back(i);
                break;
            }
        }
    }
    return ans;
}
}


void solve();
// oddloop
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t=1;
	//cin>>t;
	rep(i,t) solve();
}

void solve(){
	int N,M;
	cin>>N>>M;
	ll base=0;
	vector<vector<int>> G(N);
	vector<vector<vector<int>>> H(N);
	vector<int> U(M),V(M),C(M);
	pair<int,int> ans={INF,INF};
	ll ans_val=0;
	rep(i,M){
		cin>>U[i]>>V[i]>>C[i];
		U[i]--,V[i]--;
		G[U[i]].push_back(V[i]);
		G[V[i]].push_back(U[i]);
		base+=C[i];
		if(chmax(ans_val,(ll)C[i])){
			ans={U[i],V[i]};
		}else if(ans_val==C[i]){
			chmin(ans,{U[i],V[i]});
		}
	}
	ans_val=base;
	auto E=po167::bridges(G);
	set<pair<int,int>> s;
	for(auto x:E) s.insert(x);
	UFtree T(N);
	vector<ll> p(N);
	vector<int> seen(M);
	rep(i,M){
		if(!s.count({U[i],V[i]})&&!s.count({V[i],U[i]})){
			T.unite(U[i],V[i]);
		}else{
			seen[i]=1;
			H[T.root(U[i])].push_back({T.root(V[i]),C[i],U[i],V[i]});
			H[T.root(V[i])].push_back({T.root(U[i]),C[i],V[i],U[i]});
		}
	}
	rep(i,M){
		if(seen[i]==0){
			p[T.root(U[i])]+=C[i];
		}
	}
	int root=0;
	while(T.root(root)!=root) root++;
	vector<int> order={root};
	int ind=0;
	vector<int> pare(N,-1);
	pare[root]=-2;
	while(ind!=(int)(order.size())){
		int a=order[ind];
		for(auto x:H[a]){
			if(pare[x[0]]==-1){
				pare[x[0]]=a;
				order.push_back(x[0]);
			}
		}
		ind++;
	}
	for(ind--;ind>0;ind--){
		int a=order[ind];
		ll e=0;
		int n_p=-1,n_q=-1;
		for(auto x:H[a]){
			if(pare[a]==x[0]){
				e=x[1];
				n_p=min(x[3],x[2]);
				n_q=max(x[3],x[2]);
			}
			else{
				p[a]+=p[x[0]]+x[1];
			}
		}
		ll tmp=abs(base-e-p[a]-p[a]);
		pair<int,int> b={n_p,n_q};
		if(chmin(ans_val,tmp)){
			ans=b;
		}else if(ans_val==tmp&&ans>b) ans=b;
	}
	cout<<ans.first+1<<" "<<ans.second+1<<"\n";
}
accuracy: 0.32432432432432434
time_ms: 20
memory_kb: 7136
final_rank: 19

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;



#define SIZE 100005

struct Edge{
	bool operator<(const struct Edge &arg) const{
		if(s != arg.s){
			return s < arg.s;
		}else{
			return t < arg.t;
		}
	}
	int s,t;
	ll w;
	bool rev_FLG;
};

struct E2{
	E2(int arg_to,ll arg_w,ll arg_index){
		to = arg_to;
		w = arg_w;
		index = arg_index;
	}
	int to;
	ll w,index;
};

struct Info{
	Info(int arg_to,int arg_edge_id){
		to = arg_to;
		edge_id = arg_edge_id;
	}
	int to,edge_id;
};

int V,E,number;
int order[SIZE],lowlink[SIZE];
bool visited[SIZE],is_bridge[SIZE];
Edge edge[SIZE];
vector<Info> G[SIZE];
vector<E2> ADJ[SIZE];

int N;
int boss[SIZE],height[SIZE],num_member[SIZE];
ll minimum,SUM =0;
ll W[SIZE],S[SIZE];
vector<pair<int,int>> vec;

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

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

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

	if(boss_x == boss_y)return;

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

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

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

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

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

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

void recursive(int node_id){

	order[node_id] = number++;

	//printf("\norder[%d]:%d\n",node_id,order[node_id]);

	lowlink[node_id] = order[node_id];

	//printf("lowlink[node_id]の初期値:%d\n",lowlink[node_id]);

	int next_node;

	for(int i = 0; i < G[node_id].size(); i++){
		next_node = G[node_id][i].to;

		//printf("行先%d\n",next_node);

		if(order[next_node] == -1){ //頂点next_nodeをまだ訪れていない

			//printf("まだ訪れてないので再帰\n");
			visited[G[node_id][i].edge_id] = true;
			recursive(next_node);
			lowlink[node_id] = min(lowlink[node_id],lowlink[next_node]);

		}else if(visited[G[node_id][i].edge_id] == false){ //後退辺をまだ通っていない場合
			//printf("後退辺\n");
			lowlink[node_id] = min(lowlink[node_id],order[next_node]);
		}
	}
}

void dfs(int node_id,int pre){

	S[node_id] = W[node_id]/2;

	for(int i = 0; i < ADJ[node_id].size(); i++){

		int child = ADJ[node_id][i].to;
		if(child == pre)continue;

		dfs(child,node_id);

		ll tmp = abs(S[child]-(SUM-(ADJ[node_id][i].w+S[child])));

		int id = ADJ[node_id][i].index;

		/*printf("SUM:%lld S[child]:%lld edge.w:%lld\n",SUM,S[child],ADJ[node_id][i].w);
		printf("%d-%d tmp:%lld\n",edge[id].s+1,edge[id].t+1,tmp);*/

		if(tmp < minimum){

			minimum = tmp;


			vec.clear();
			if(!edge[id].rev_FLG){
				vec.push_back(make_pair(edge[id].s,edge[id].t));
			}else{

				vec.push_back(make_pair(edge[id].t,edge[id].s));
			}
		}else if(tmp == minimum){

			if(!edge[id].rev_FLG){
				vec.push_back(make_pair(edge[id].s,edge[id].t));
			}else{

				vec.push_back(make_pair(edge[id].t,edge[id].s));
			}
		}

		S[node_id] += S[child];
		S[node_id] += ADJ[node_id][i].w; //★★注意★★
	}
}

int main(){

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

	for(int i = 0; i < E; i++){
		scanf("%d %d %lld",&edge[i].s,&edge[i].t,&edge[i].w);

		SUM += edge[i].w;

		edge[i].s--;
		edge[i].t--;

		edge[i].rev_FLG = false;
		if(edge[i].s > edge[i].t){
			swap(edge[i].s,edge[i].t);
			edge[i].rev_FLG = true;
		}
		G[edge[i].s].push_back(Info(edge[i].t,i));
		G[edge[i].t].push_back(Info(edge[i].s,i));
	}

	sort(edge,edge+E);

	number = 0;

	for(int i = 0; i < V; i++){
		order[i] = -1;
		lowlink[i] = -1;
	}

	//★dfsで通らなかった辺が後退辺!!★
	for(int i = 0; i < E; i++){
		visited[i] = false;
	}

	recursive(0);

	/*for(int i = 0; i < V; i++){
		printf("ord[%d]:%d low[i]:%d\n",i,order[i],lowlink[i]);
	}*/

	int from,to;

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

		is_bridge[i] = false;
	}

	minimum = SUM;

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

		boss[i] = i;
		height[i] = 0;
		W[i] = 0;
	}

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

		from = edge[i].s;
		to = edge[i].t;

		if(order[edge[i].s] > order[edge[i].t]){
			swap(from,to);
		}

		if(order[from] < lowlink[to]){ //橋

			is_bridge[i] = true;
		}else{ //橋でない:除去してもグラフは連結

			W[edge[i].s] += edge[i].w;
			W[edge[i].t] += edge[i].w;

			ll tmp = SUM-edge[i].w;
			if(tmp < minimum){

				minimum = tmp;
				vec.clear();

				if(!edge[i].rev_FLG){
					vec.push_back(make_pair(edge[i].s,edge[i].t));
				}else{

					vec.push_back(make_pair(edge[i].t,edge[i].s));
				}
			}else if(tmp == minimum){

				if(!edge[i].rev_FLG){
					vec.push_back(make_pair(edge[i].s,edge[i].t));
				}else{

					vec.push_back(make_pair(edge[i].t,edge[i].s));
				}
			}
		}
	}

	/*二重辺連結成分+橋のグラフを作る*/

	for(int i = 0; i < E; i++){
		if(is_bridge[i])continue;

		unite(edge[i].s,edge[i].t);
	}


	for(int i = 0; i < E; i++){
		if(!is_bridge[i])continue;

		int a = get_boss(edge[i].s);
		int b = get_boss(edge[i].t);

		ADJ[a].push_back(E2(b,edge[i].w,i));
		ADJ[b].push_back(E2(a,edge[i].w,i));
	}

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

			dfs(i,-1);
			break;
		}
	}

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


	printf("%d %d\n",vec[0].first+1,vec[0].second+1);


	return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 24148
final_rank: 4

code_snippet: #include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const long long INF = 1LL << 60;    // 1.15x10^18

long long tot;
pair<long long, pair<int, int>> mi;

using edge = pair<int, long long>;
using Graph = vector<vector<edge>>;
struct LowLink {
    const Graph &g;
    vector<int> aps;                // articulation points
    vector<pair<int, int>> brs;     // brides
    vector<int> seen, ord, low;
    vector<long long> dp;
    LowLink(const Graph &g) : g(g) { }
    int dfs(int u, int p = -1, int time = 0) {
        seen[u] = true;
        ord[u] = low[u] = time++;
        bool exists = false;
        int cnt_ch = 0;
        for (auto &e: g[u]) {
            int v = e.first; long long w = e.second;
            mi = min(mi, {tot - w, minmax(u, v)});
            if (!seen[v]) {
                cnt_ch++;
                time = dfs(v, u, time);
                dp[u] += w + dp[v];
                low[u] = min(low[u], low[v]);   // forward edge
                if (ord[u] < low[v]) {
                    brs.emplace_back(u, v);
                    mi = min(mi, {abs(tot - w - dp[v] * 2), minmax(u, v)});
                }
                if (ord[u] <= low[v]) exists = true;
            } else if (v != p) {
                dp[u] += w;
                low[u] = min(low[u], ord[v]);   // back edge
            }
        }
        if ((p == -1 && cnt_ch > 1) || (p != -1 && exists)) aps.emplace_back(u);
        return time;
    }
    void build() {
        int n = g.size();
        seen.assign(n, 0); ord.resize(n); low.resize(n); dp.resize(n);
        aps.clear(); brs.clear();
        for (int v = 0; v < n; ++v) if (!seen[v]) dfs(v);
        for (auto &br: brs) if (br.first > br.second) swap(br.first, br.second);
        sort(brs.begin(), brs.end());
        sort(aps.begin(), aps.end());
    }
};

int main() {
    mi.first = INF;
    int n, m; cin >> n >> m;
    Graph g(n);
    while (m--) {
        int u, v, w; cin >> u >> v >> w; u--, v--;
        g[u].emplace_back(v, w);
        g[v].emplace_back(u, w);
        tot += w;
    }
    LowLink ll(g);
    ll.build();
    cout << mi.second.first + 1 << " " << mi.second.second + 1 << endl;
    return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 14444
final_rank: 6

code_snippet: #include <algorithm>
#include <iostream>
#include <vector>
#include <tuple>
#include <map>
using namespace std;
const long long INF = 1LL << 60;    // 1.15x10^18

struct LowLink {
    const vector<vector<int>> &g;
    vector<int> aps;                // articulation points
    vector<pair<int, int>> brs;     // brides
    vector<int> seen, ord, low;
    LowLink(const vector<vector<int>> &g) : g(g) { }
    int dfs(int u, int p = -1, int time = 0) {
        seen[u] = true;
        ord[u] = low[u] = time++;
        bool exists = false;
        int cnt_ch = 0;
        for (int v: g[u]) {
            if (!seen[v]) {
                cnt_ch++;
                time = dfs(v, u, time);
                low[u] = min(low[u], low[v]);   // forward edge
                if (ord[u] < low[v]) brs.emplace_back(u, v);
                if (ord[u] <= low[v]) exists = true;
            } else if (v != p) {
                low[u] = min(low[u], ord[v]);   // back edge
            }
        }
        if ((p == -1 && cnt_ch > 1) || (p != -1 && exists)) aps.emplace_back(u);
        return time;
    }
    void build() {
        int n = g.size();
        seen.assign(n, 0); ord.resize(n); low.resize(n);
        aps.clear(); brs.clear();
        for (int v = 0; v < n; ++v) if (!seen[v]) dfs(v);
        for (auto &br: brs) if (br.first > br.second) swap(br.first, br.second);
        sort(brs.begin(), brs.end());
        sort(aps.begin(), aps.end());
    }
};
struct TwoEdgeConnectedComponents : LowLink {
    using LL = LowLink;
    vector<int> comp;
    TwoEdgeConnectedComponents(const vector<vector<int>> &g) : LL(g) { }
    void dfs(int u, int p, int &k) {
        if (~p && ord[p] >= low[u]) comp[u] = comp[p];
        else comp[u] = k++;
        for (int v: g[u]) if (comp[v] == -1) dfs(v, u, k);
    }
    vector<vector<int>> build() {
        LL::build();
        comp.assign(g.size(), -1);
        int k = 0;
        for (int i = 0; i < (int)g.size(); i++) if (!~comp[i]) dfs(i, -1, k);
        vector<vector<int>> t_comp(k);
        // for (auto &p: brs) {
        //     int x = comp[p.first], y = comp[p.second];
        //     t_comp[x].emplace_back(y);
        //     t_comp[y].emplace_back(x);
        // }
        return t_comp;
    }
    int operator[](int k) { return comp[k]; }
};

using edge = pair<int, long long>;
using Graph = vector<vector<edge>>;
Graph tree;
long long tot;
vector<long long> cost_v;
pair<long long, pair<int, int>> mi;
map<pair<int, int>, pair<int, int>> rev;
long long dfs(int u, int p, int cost_e) {
    long long sum = cost_v[u];
    for (auto &e: tree[u]) if (e.first != p) {
        sum += e.second + dfs(e.first, u, e.second);
    }
    mi = min(mi, {abs(tot - cost_e - sum * 2), rev[minmax(u, p)]});
    return sum;
}

int main() {
    int n, m; cin >> n >> m;
    vector<vector<int>> g(n);
    vector<tuple<int, int, int>> edges;
    for (int i = 0; i < m; i++) {
        int u, v, w; cin >> u >> v >> w; u--, v--;
        g[u].emplace_back(v);
        g[v].emplace_back(u);
        edges.emplace_back(u, v, w);
        tot += w;
    }
    TwoEdgeConnectedComponents bcc(g);
    int sz = bcc.build().size();
    tree.resize(sz), cost_v.resize(sz);
    mi.first = INF;
    for (auto &t: edges) {
        int a, b, c; tie(a, b, c) = t;
        int x = bcc[a], y = bcc[b];
        if (x == y) {
            cost_v[x] += c;
            mi = min(mi, {tot - c, minmax(a, b)});
        } else {
            tree[x].emplace_back(y, c);
            tree[y].emplace_back(x, c);
            rev[minmax(x, y)] = minmax(a, b);
        }
    }
    dfs(0, -1, 0);
    cout << mi.second.first + 1 << " " << mi.second.second + 1 << endl;
    return 0;
}
accuracy: 1
time_ms: 170
memory_kb: 27192
final_rank: 11

code_snippet: #include <algorithm>
#include <iostream>
#include <vector>
#include <tuple>
#include <map>
using namespace std;
const long long INF = 1LL << 60;    // 1.15x10^18

struct LowLink {
    const vector<vector<int>> &g;
    vector<int> aps;                // articulation points
    vector<pair<int, int>> brs;     // brides
    vector<int> seen, ord, low;
    LowLink(const vector<vector<int>> &g) : g(g) { }
    int dfs(int u, int p = -1, int time = 0) {
        seen[u] = true;
        ord[u] = low[u] = time++;
        bool exists = false;
        int cnt_ch = 0;
        for (int v: g[u]) {
            if (!seen[v]) {
                cnt_ch++;
                time = dfs(v, u, time);
                low[u] = min(low[u], low[v]);   // forward edge
                if (ord[u] < low[v]) brs.emplace_back(u, v);
                if (ord[u] <= low[v]) exists = true;
            } else if (v != p) {
                low[u] = min(low[u], ord[v]);   // back edge
            }
        }
        if ((p == -1 && cnt_ch > 1) || (p != -1 && exists)) aps.emplace_back(u);
        return time;
    }
    void build() {
        int n = g.size();
        seen.assign(n, 0); ord.resize(n); low.resize(n);
        aps.clear(); brs.clear();
        for (int v = 0; v < n; ++v) if (!seen[v]) dfs(v);
        for (auto &br: brs) if (br.first > br.second) swap(br.first, br.second);
        sort(brs.begin(), brs.end());
        sort(aps.begin(), aps.end());
    }
};
struct TwoEdgeConnectedComponents : LowLink {
    using LL = LowLink;
    vector<int> comp;
    TwoEdgeConnectedComponents(const vector<vector<int>> &g) : LL(g) { }
    void dfs(int u, int p, int &k) {
        if (~p && ord[p] >= low[u]) comp[u] = comp[p];
        else comp[u] = k++;
        for (int v: g[u]) if (comp[v] == -1) dfs(v, u, k);
    }
    vector<vector<int>> build() {
        LL::build();
        comp.assign(g.size(), -1);
        int k = 0;
        for (int i = 0; i < (int)g.size(); i++) if (!~comp[i]) dfs(i, -1, k);
        vector<vector<int>> t_comp(k);
        for (auto &p: brs) {
            int x = comp[p.first], y = comp[p.second];
            t_comp[x].emplace_back(y);
            t_comp[y].emplace_back(x);
        }
        return t_comp;
    }
    int operator[](int k) { return comp[k]; }
};

using edge = pair<int, long long>;
using Graph = vector<vector<edge>>;
Graph tree;
long long tot;
vector<long long> cost_v;
pair<long long, pair<int, int>> mi;
map<pair<int, int>, pair<int, int>> rev;
long long dfs(int u, int p, int cost_e) {
    long long sum = cost_v[u];
    for (auto &e: tree[u]) if (e.first != p) {
        sum += e.second + dfs(e.first, u, e.second);
    }
    mi = min(mi, {abs(tot - cost_e - sum * 2), rev[minmax(u, p)]});
    return sum;
}

int main() {
    int n, m; cin >> n >> m;
    vector<vector<int>> g(n);
    vector<tuple<int, int, int>> edges;
    for (int i = 0; i < m; i++) {
        int u, v, w; cin >> u >> v >> w; u--, v--;
        g[u].emplace_back(v);
        g[v].emplace_back(u);
        edges.emplace_back(u, v, w);
        tot += w;
    }
    TwoEdgeConnectedComponents bcc(g);
    int sz = bcc.build().size();
    tree.resize(sz), cost_v.resize(sz);
    mi.first = INF;
    for (auto &t: edges) {
        int a, b, c; tie(a, b, c) = t;
        int x = bcc[a], y = bcc[b];
        if (x == y) {
            cost_v[x] += c;
            mi = min(mi, {tot - c, minmax(a, b)});
        } else {
            rev[minmax(x, y)] = minmax(a, b);
            tree[x].emplace_back(y, c);
            tree[y].emplace_back(x, c);
        }
    }
    dfs(0, -1, 0);
    cout << mi.second.first + 1 << " " << mi.second.second + 1 << endl;
    return 0;
}
accuracy: 1
time_ms: 190
memory_kb: 27220
final_rank: 12

code_snippet: #include "iostream"
#include "climits"
#include "list"
#include "queue"
#include "stack"
#include "set"
#include "functional"
#include "algorithm"
#include "string"
#include "map"
#include "unordered_map"
#include "unordered_set"
#include "iomanip"
#include "cmath"
#include "random"
#include "bitset"
#include "cstdio"
#include "numeric"
#include "cassert"
#include "functional"
#include "ctime"

using namespace std;

constexpr long long int MOD = 1000000007;
//constexpr int MOD = 1000000007;
//constexpr int MOD = 998244353;
//constexpr long long int MOD = 998244353;
constexpr double EPS = 1e-8;

//int N, M, K, H, W, L, R;
long long int N, M, K, H, W, L, R;

template< typename T >
struct edge {
	int src, to;
	T cost;

	edge(int to, T cost) : src(-1), to(to), cost(cost) {}

	edge(int src, int to, T cost) : src(src), to(to), cost(cost) {}

	edge &operator=(const int &x) {
		to = x;
		return *this;
	}

	operator int() const { return to; }
};

template< typename T >
using Edges = vector< edge< T > >;
template< typename T >
using WeightedGraph = vector< Edges< T > >;
using UnWeightedGraph = vector< vector< int > >;
template< typename T >
using Matrix = vector< vector< T > >;

template< typename G >
struct LowLink {
	const G &g;
	vector< int > used, ord, low;
	vector< int > articulation;
	vector< pair< int, int > > bridge;

	LowLink(const G &g) : g(g) {}

	int dfs(int idx, int k, int par) {
		used[idx] = true;
		ord[idx] = k++;
		low[idx] = ord[idx];
		bool is_articulation = false;
		int cnt = 0;
		for (auto &to : g[idx]) {
			if (!used[to]) {
				++cnt;
				k = dfs(to, k, idx);
				low[idx] = min(low[idx], low[to]);
				is_articulation |= ~par && low[to] >= ord[idx];
				if (ord[idx] < low[to]) bridge.emplace_back(minmax(idx, (int)to));
			}
			else if (to != par) {
				low[idx] = min(low[idx], ord[to]);
			}
		}
		is_articulation |= par == -1 && cnt > 1;
		if (is_articulation) articulation.push_back(idx);
		return k;
	}

	virtual void build() {
		used.assign(g.size(), 0);
		ord.assign(g.size(), 0);
		low.assign(g.size(), 0);
		int k = 0;
		for (int i = 0; i < g.size(); i++) {
			if (!used[i]) k = dfs(i, k, -1);
		}
	}
};

void Calculate_Depth(vector<vector<pair<int,int>>>&edge, vector<int>&depth, int stnode) {
	depth[stnode] = 0;
	queue<int>Q;
	Q.push(stnode);
	while (!Q.empty()) {
		int cn = Q.front();
		Q.pop();
		for (auto i : edge[cn]) {
			if (depth[i.first] > depth[cn] + 1) {
				depth[i.first] = depth[cn] + 1;
				Q.push(i.first);
			}
		}
	}
	return;
}

void Search(vector<vector<pair<int, int>>>&edge, vector<int>&depth, vector<int>&used, vector<int>&visited,vector<long long int>&sum, int node = 0) {
	for (auto i : edge[node]) {
		if (depth[i.first] < depth[node])continue;
		if (depth[i.first] == depth[node]) {
			if (i.first < node)continue;
		}
		if (!visited[i.first]) {
			visited[i.first]++;
			Search(edge, depth, used, visited, sum, i.first);
		}
		if (depth[i.first] > depth[node]) {
			if (!used[i.first]) {
				used[i.first]++;
				sum[node] += sum[i.first];
			}
		}
		sum[node] += i.second;
	}
}

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

	cin >> N >> M;
	UnWeightedGraph g(N);
	vector<vector<pair<int, int>>>e(N);
	vector<int>depth(N, MOD);
	long long int mx = 0;
	for (int i = 0; i < M; i++) {
		cin >> L >> R >> K;
		L--, R--;
		mx += K;
		e[L].push_back({ R,K });
		e[R].push_back({ L,K });
		g[L].push_back(R);
		g[R].push_back(L);
	}
	Calculate_Depth(e, depth, 0);
	LowLink< UnWeightedGraph > lowlink(g);
	lowlink.build();
	sort(lowlink.bridge.begin(), lowlink.bridge.end());
	vector<long long int>sum(N);
	vector<int>visited(N);
	vector<int>used(N);
	Search(e, depth, used,visited, sum);
	unordered_set<long long int>st;
	for (auto i : lowlink.bridge)st.insert( min(i.first,i.second)*MOD+max(i.first,i.second) );
	pair<int, int>ans(MOD, MOD);
	long long int answeight = MOD * MOD;
	for (int i = 0; i < N; i++) {
		for (auto j : e[i]) {
			if (answeight > mx - j.second) {
				answeight = mx - j.second;
				ans = { min(i,j.first),max(i,j.first) };
			}
			if (answeight >= mx - j.second) {
				answeight = mx - j.second;
				ans = min(ans, { min(i,j.first),max(i,j.first) });
			}
			if (depth[j.first] <= depth[i])continue;
			if (st.find( min(i,j.first)*MOD+max(i,j.first) ) == st.end())continue;
			long long int weight = abs(mx - j.second - sum[j.first] * 2);
			if (answeight >= weight) {
				if (answeight > weight) {
					ans = { min(i,j.first),max(i,j.first) };
				}
				answeight = weight;
				ans = min(ans, { min(i,j.first),max(i,j.first) });
			}
		}
	}
	cout << ans.first + 1 << " " << ans.second + 1 << endl;
}
accuracy: 1
time_ms: 90
memory_kb: 22828
final_rank: 8

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
//INSERT ABOVE HERE
const Int INF = 1e17;
struct edge{
  Int w,u,v,to;
  edge(){}
  edge(Int w,Int u,Int v,Int to):w(w),u(u),v(v),to(to){}
  bool operator<(const edge &a)const{
    if(w!=a.w) return w<a.w;
    if(u!=a.u) return u<a.u;
    return v<a.v;
  }
};

struct LowLink{
  Int n,cnt;
  vector<vector<edge> > G;
  vector<Int> sum,used,pre,low,par;
  
  LowLink(Int n):n(n),cnt(0),G(n),sum(n),used(n),pre(n),low(n),par(n){}

  void add_edge(Int u,Int v,Int w){
    G[u].emplace_back(w,u,v,v);
    G[v].emplace_back(w,u,v,u);
  }

  void dfs(Int v,Int p,Int d=0){
    pre[v]=low[v]=d;
    used[v]=1;
    for(auto e:G[v]){
      Int u=e.to;
      if(!used[u]){
	par[u]=v;
	dfs(u,v,d+1);
	low[v]=min(low[v],low[u]);
	sum[v]+=sum[u]+e.w;
      }else if(u!=p){
	low[v]=min(low[v],pre[u]);
        sum[u]+=e.w;
      }      
    }
  }
  
  edge build(){
    dfs(0,-1);
    edge ans(INF,0,0,0);
    Int all=sum[0];
    for(Int i=0;i<n;i++){
      for(edge e:G[i]){
	Int u=e.u,v=e.v;
	if(par[u]==v){	    
	  Int a=sum[u];
	  Int b=all-sum[u]-e.w;
	  edge e2(abs(a-b),u,v,-1);
	  if(pre[u]==low[u]&&e2<ans) ans=e2;
	}
	if(par[v]==u){
	  Int a=sum[v];
	  Int b=all-sum[v]-e.w;
	  edge e2(abs(a-b),u,v,-1);
	  if(pre[v]==low[v]&&e2<ans) ans=e2;
	}
	{
	  edge e2(all-e.w,u,v,-1);
	  if(e2<ans) ans=e2;	  
	}
      }
    }
    return ans;
  }
  
};



signed main(){
  Int n,m;
  cin>>n>>m;
  LowLink G(n);
  Int sum=0;
  for(Int i=0;i<m;i++){
    Int u,v,w;
    cin>>u>>v>>w;
    u--;v--;
    G.add_edge(u,v,w);
    sum+=w;
  }
  edge ans=G.build();
  //cout<<sum<<":"<<G.sum[0]<<endl;
  //cout<<ans.w<<endl;
  assert(sum==G.sum[0]);
  cout<<ans.u+1<<" "<<ans.v+1<<endl;
  return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 18296
final_rank: 7

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define NUM 100005

struct Edge{
	Edge(){
		s = t = 0;
		cost = 0;
	}
	Edge(int arg_s,int arg_t,ll arg_cost){
		s = arg_s;
		t = arg_t;
		cost = arg_cost;
	}
	bool operator<(const struct Edge &arg) const{
		if(s != arg.s){
			return s < arg.s;
		}else{
			return t < arg.t;
		}
	}
	int s,t;
	ll cost;
};

struct Info{
	Info(int arg_to,int arg_edge_id,int arg_port = -1){
		to = arg_to;
		edge_id = arg_edge_id;
		port = arg_port;
	}
	int to,edge_id,port;
};

struct Node{

	int parent,edge_id;
	ll W,edge_cost;
};

int V,E,number;
int order[NUM],lowlink[NUM];
int height[NUM],parent[NUM],connector[NUM];
ll sum_weight[NUM];
bool visited[NUM],is_bridge[NUM];
Edge edge[NUM];
Node nodes[NUM];
vector<Info> G[NUM],Group_G[NUM];

int find(int x){
	if(x == parent[x])return x;
	else{
		return parent[x] = find(parent[x]);
	}
}

bool isSame(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(height[x] < height[y]){
		parent[x] = y;
	}else{
		parent[y] = x;
		if(height[x] == height[y])height[x]++;
	}
}

void recursive(int node_id){

	order[node_id] = number++;
	lowlink[node_id] = order[node_id];

	int next_node;

	for(int i = 0; i < G[node_id].size(); i++){
		next_node = G[node_id][i].to;

		if(order[next_node] == -1){ //頂点next_nodeをまだ訪れていない

			visited[G[node_id][i].edge_id] = true;
			recursive(next_node);
			lowlink[node_id] = min(lowlink[node_id],lowlink[next_node]);

		}else if(visited[G[node_id][i].edge_id] == false){ //後退辺をまだ通っていない場合
			lowlink[node_id] = min(lowlink[node_id],order[next_node]);
		}
	}
}

//部分木の総コストを計算
ll dfs(int group_id,int parent){

	ll ret = sum_weight[group_id];

	for(int i = 0; i < Group_G[group_id].size(); i++){
		if(Group_G[group_id][i].to == parent){

			nodes[group_id].parent = parent;
			nodes[group_id].edge_id = Group_G[group_id][i].edge_id;
			nodes[group_id].edge_cost = edge[Group_G[group_id][i].edge_id].cost;
			continue;
		}
		ret += dfs(Group_G[group_id][i].to,group_id)+edge[Group_G[group_id][i].edge_id].cost;
	}

	nodes[group_id].W = ret; //部分木の総コスト

	return ret;
}

int main(){

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

	ll sum = 0;

	for(int i = 0; i < E; i++){
		scanf("%d %d %lld",&edge[i].s,&edge[i].t,&edge[i].cost);

		edge[i].s--;
		edge[i].t--;

		sum += edge[i].cost;

		if(edge[i].s > edge[i].t){
			swap(edge[i].s,edge[i].t);
		}

		G[edge[i].s].push_back(Info(edge[i].t,i));
		G[edge[i].t].push_back(Info(edge[i].s,i));
	}

	ll minimum = sum;
	//printf("sum:%lld\n",sum);
	int ans_from,ans_to;

	//1つの辺を削除する場合を調べる
	for(int i = 0; i < E; i++){

		if(minimum > sum-edge[i].cost){
			minimum = sum-edge[i].cost;
			ans_from = edge[i].s;
			ans_to = edge[i].t;
		}
	}

	sort(edge,edge+E);

	number = 0;

	for(int i = 0; i < V; i++){
		order[i] = -1;
		lowlink[i] = -1;
	}

	//★dfsで通らなかった辺が後退辺!!★
	for(int i = 0; i < E; i++){
		visited[i] = false;
	}

	recursive(0);

	int from,to;

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

		is_bridge[i] = false;
	}

	int num_bridge = 0;

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

		from = edge[i].s;
		to = edge[i].t;

		if(order[edge[i].s] > order[edge[i].t]){
			swap(from,to);
		}

		if(order[from] < lowlink[to]){
			num_bridge++;
			is_bridge[i] = true;
		}
	}

	if(num_bridge == 0){
		printf("%d %d\n",ans_from+1,ans_to+1);
		return 0;
	}

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

		parent[i] = i;
		height[i] = 0;
	}

	//union-findでグループ分け
	for(int i = 0; i < E; i++){
		if(is_bridge[i]){
			continue;
		}

		unite(edge[i].s,edge[i].t);
	}

	//グループごとの重さを調べる
	for(int i = 0; i < V; i++){

		sum_weight[i] = 0;
	}
	for(int i = 0; i < E; i++){
		if(isSame(edge[i].s,edge[i].t)){
			sum_weight[find(edge[i].s)] += edge[i].cost;
		}
	}

	//グループ間のエッジを張る(すべて橋のはず)
	for(int i = 0; i < E; i++){

		if(isSame(edge[i].s,edge[i].t)){
			continue;
		}

		Group_G[find(edge[i].s)].push_back(Info(find(edge[i].t),i));
		Group_G[find(edge[i].t)].push_back(Info(find(edge[i].s),i));
	}

	int root;
	//木にする
	for(int i = 0; i < V; i++){
		if(i == find(i)){
			root = i;
			dfs(i,-1);
			break;
		}
	}

	for(int i = 0; i < V; i++){
		if(i != find(i) ||i == root)continue;

		if(minimum > abs(sum-(nodes[i].W+nodes[i].edge_cost)-nodes[i].W)){

			minimum = abs(sum-(nodes[i].W+nodes[i].edge_cost)-nodes[i].W);
			ans_from = min(edge[nodes[i].edge_id].s,edge[nodes[i].edge_id].t);
			ans_to =  max(edge[nodes[i].edge_id].s,edge[nodes[i].edge_id].t);

		}else if(minimum == abs(sum-(nodes[i].W+nodes[i].edge_cost)-nodes[i].W)){

			if(ans_from > min(min(edge[nodes[i].edge_id].s,edge[nodes[i].edge_id].t),nodes[i].parent)){

				ans_from = min(edge[nodes[i].edge_id].s,edge[nodes[i].edge_id].t);
				ans_to =  max(edge[nodes[i].edge_id].s,edge[nodes[i].edge_id].t);

			}else if(ans_from == min(min(edge[nodes[i].edge_id].s,edge[nodes[i].edge_id].t),nodes[i].parent)
					&& ans_to > max(max(edge[nodes[i].edge_id].s,edge[nodes[i].edge_id].t),nodes[i].parent)){

				ans_to = max(edge[nodes[i].edge_id].s,edge[nodes[i].edge_id].t);
			}
		}
	}

	printf("%d %d\n",ans_from+1,ans_to+1);

	return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 22480
final_rank: 3

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define NUM 100005

struct Edge{
	Edge(){
		s = t = 0;
		cost = 0;
	}
	Edge(int arg_s,int arg_t,ll arg_cost){
		s = arg_s;
		t = arg_t;
		cost = arg_cost;
	}
	bool operator<(const struct Edge &arg) const{
		if(s != arg.s){
			return s < arg.s;
		}else{
			return t < arg.t;
		}
	}
	int s,t;
	ll cost;
};

struct Info{
	Info(int arg_to,int arg_edge_id){
		to = arg_to;
		edge_id = arg_edge_id;
	}
	int to,edge_id;
};

struct Node{

	int parent;
	ll W,edge_cost;
};

int V,E,number;
int order[NUM],lowlink[NUM];
int height[NUM],parent[NUM];
ll sum_weight[NUM];
bool visited[NUM],is_bridge[NUM];
Edge edge[NUM];
Node nodes[NUM];
vector<Info> G[NUM],Group_G[NUM];

int find(int x){
	if(x == parent[x])return x;
	else{
		return parent[x] = find(parent[x]);
	}
}

bool isSame(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(height[x] < height[y]){
		parent[x] = y;
	}else{
		parent[y] = x;
		if(height[x] == height[y])height[x]++;
	}
}

void recursive(int node_id){

	order[node_id] = number++;
	lowlink[node_id] = order[node_id];

	int next_node;

	for(int i = 0; i < G[node_id].size(); i++){
		next_node = G[node_id][i].to;

		if(order[next_node] == -1){ //頂点next_nodeをまだ訪れていない

			visited[G[node_id][i].edge_id] = true;
			recursive(next_node);
			lowlink[node_id] = min(lowlink[node_id],lowlink[next_node]);

		}else if(visited[G[node_id][i].edge_id] == false){ //後退辺をまだ通っていない場合
			lowlink[node_id] = min(lowlink[node_id],order[next_node]);
		}
	}
}

//部分木の総コストを計算
ll dfs(int group_id,int parent){

	ll ret = sum_weight[group_id];

	for(int i = 0; i < Group_G[group_id].size(); i++){
		if(Group_G[group_id][i].to == parent){
			if(group_id == edge[Group_G[group_id][i].edge_id].s){

				nodes[group_id].parent = edge[Group_G[group_id][i].edge_id].t;
			}else{
				nodes[group_id].parent = edge[Group_G[group_id][i].edge_id].s;
			}

			nodes[group_id].edge_cost = edge[Group_G[group_id][i].edge_id].cost;
			continue;
		}
		ret += dfs(Group_G[group_id][i].to,group_id)+edge[Group_G[group_id][i].edge_id].cost;
	}

	nodes[group_id].W = ret; //部分木の総コスト

	return ret;
}

int main(){

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

	ll sum = 0;

	for(int i = 0; i < E; i++){
		scanf("%d %d %lld",&edge[i].s,&edge[i].t,&edge[i].cost);

		edge[i].s--;
		edge[i].t--;

		sum += edge[i].cost;

		if(edge[i].s > edge[i].t){
			swap(edge[i].s,edge[i].t);
		}
		G[edge[i].s].push_back(Info(edge[i].t,i));
		G[edge[i].t].push_back(Info(edge[i].s,i));
	}

	ll minimum = sum;

	int ans_from,ans_to;

	//1つの辺を削除する場合を調べる
	for(int i = 0; i < E; i++){

		if(minimum > abs(sum-2*edge[i].cost)){
			minimum = abs(sum-2*edge[i].cost);
			ans_from = edge[i].s;
			ans_to = edge[i].t;
		}
	}

	sort(edge,edge+E);

	number = 0;

	for(int i = 0; i < V; i++){
		order[i] = -1;
		lowlink[i] = -1;
	}

	//★dfsで通らなかった辺が後退辺!!★
	for(int i = 0; i < E; i++){
		visited[i] = false;
	}

	recursive(0);

	int from,to;

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

		is_bridge[i] = false;
	}

	int num_bridge = 0;

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

		from = edge[i].s;
		to = edge[i].t;

		if(order[edge[i].s] > order[edge[i].t]){
			swap(from,to);
		}

		if(order[from] < lowlink[to]){
			num_bridge++;
			is_bridge[i] = true;
		}
	}

	if(num_bridge == 0){
		printf("%d %d\n",ans_from+1,ans_to+1);
		return 0;
	}

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

		parent[i] = i;
		height[i] = 0;
	}

	//union-findでグループ分け
	for(int i = 0; i < E; i++){
		if(is_bridge[i]){
			continue;
		}

		unite(edge[i].s,edge[i].t);
	}

	//グループごとの重さを調べる
	for(int i = 0; i < V; i++){

		sum_weight[i] = 0;
	}
	for(int i = 0; i < E; i++){
		if(isSame(edge[i].s,edge[i].t)){
			sum_weight[find(edge[i].s)] += edge[i].cost;
		}
	}

	//グループ間のエッジを張る(すべて橋のはず)
	for(int i = 0; i < E; i++){

		if(isSame(edge[i].s,edge[i].t)){
			continue;
		}

		Group_G[find(edge[i].s)].push_back(Info(find(edge[i].t),i));
		Group_G[find(edge[i].t)].push_back(Info(find(edge[i].s),i));
	}

	int root;
	//木にする
	for(int i = 0; i < V; i++){
		if(i == find(i)){
			root = i;
			dfs(i,-1);
			break;
		}
	}

	for(int i = 0; i < V; i++){
		if(i != find(i) ||i == root)continue;

		if(minimum > abs(sum-(nodes[i].W+nodes[i].edge_cost)-nodes[i].W)){

			minimum = abs(sum-(nodes[i].W+nodes[i].edge_cost)-nodes[i].W);
			ans_from = min(i,nodes[i].parent);
			ans_to = max(i,nodes[i].parent);

		}else if(minimum == abs(sum-(nodes[i].W+nodes[i].edge_cost)-nodes[i].W)){

			if(ans_from > min(i,nodes[i].parent)){

				ans_from = min(i,nodes[i].parent);
				ans_to = max(i,nodes[i].parent);

			}else if(ans_from == min(i,nodes[i].parent) && ans_to > max(i,nodes[i].parent)){

				ans_to = max(i,nodes[i].parent);
			}
		}
	}

	printf("%d %d\n",ans_from+1,ans_to+1);

	return 0;
}
accuracy: 0.6216216216216216
time_ms: 60
memory_kb: 21544
final_rank: 14

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define NUM 100005

struct Edge{
	Edge(){
		s = t = 0;
		cost = 0;
	}
	Edge(int arg_s,int arg_t,ll arg_cost){
		s = arg_s;
		t = arg_t;
		cost = arg_cost;
	}
	bool operator<(const struct Edge &arg) const{
		if(s != arg.s){
			return s < arg.s;
		}else{
			return t < arg.t;
		}
	}
	int s,t;
	ll cost;
};

struct Info{
	Info(int arg_to,int arg_edge_id){
		to = arg_to;
		edge_id = arg_edge_id;
	}
	int to,edge_id;
};

struct Node{

	int parent;
	ll W,edge_cost;
};

int V,E,number;
int order[NUM],lowlink[NUM];
int height[NUM],parent[NUM];
ll sum_weight[NUM];
bool visited[NUM],is_bridge[NUM];
Edge edge[NUM];
Node nodes[NUM];
vector<Info> G[NUM],Group_G[NUM];

int find(int x){
	if(x == parent[x])return x;
	else{
		return parent[x] = find(parent[x]);
	}
}

bool isSame(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(height[x] < height[y]){
		parent[x] = y;
	}else{
		parent[y] = x;
		if(height[x] == height[y])height[x]++;
	}
}

void recursive(int node_id){

	order[node_id] = number++;
	lowlink[node_id] = order[node_id];

	int next_node;

	for(int i = 0; i < G[node_id].size(); i++){
		next_node = G[node_id][i].to;

		if(order[next_node] == -1){ //頂点next_nodeをまだ訪れていない

			visited[G[node_id][i].edge_id] = true;
			recursive(next_node);
			lowlink[node_id] = min(lowlink[node_id],lowlink[next_node]);

		}else if(visited[G[node_id][i].edge_id] == false){ //後退辺をまだ通っていない場合
			lowlink[node_id] = min(lowlink[node_id],order[next_node]);
		}
	}
}

//部分木の総コストを計算
ll dfs(int group_id,int parent){

	ll ret = sum_weight[group_id];

	for(int i = 0; i < Group_G[group_id].size(); i++){
		if(Group_G[group_id][i].to == parent){
			if(group_id == edge[Group_G[group_id][i].edge_id].s){

				nodes[group_id].parent = edge[Group_G[group_id][i].edge_id].t;
			}else{
				nodes[group_id].parent = edge[Group_G[group_id][i].edge_id].s;
			}

			nodes[group_id].edge_cost = edge[Group_G[group_id][i].edge_id].cost;
			continue;
		}
		ret += dfs(Group_G[group_id][i].to,group_id)+edge[Group_G[group_id][i].edge_id].cost;
	}

	nodes[group_id].W = ret; //部分木の総コスト

	return ret;
}

int main(){

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

	ll sum = 0;

	for(int i = 0; i < E; i++){
		scanf("%d %d %lld",&edge[i].s,&edge[i].t,&edge[i].cost);

		edge[i].s--;
		edge[i].t--;

		sum += edge[i].cost;

		if(edge[i].s > edge[i].t){
			swap(edge[i].s,edge[i].t);
		}
		G[edge[i].s].push_back(Info(edge[i].t,i));
		G[edge[i].t].push_back(Info(edge[i].s,i));
	}

	ll minimum = sum;
	int ans_from,ans_to;

	//1つの辺を削除する場合を調べる
	for(int i = 0; i < E; i++){

		if(minimum > sum-edge[i].cost){
			minimum = sum-edge[i].cost;
			ans_from = edge[i].s;
			ans_to = edge[i].t;
		}
	}

	sort(edge,edge+E);

	number = 0;

	for(int i = 0; i < V; i++){
		order[i] = -1;
		lowlink[i] = -1;
	}

	//★dfsで通らなかった辺が後退辺!!★
	for(int i = 0; i < E; i++){
		visited[i] = false;
	}

	recursive(0);

	int from,to;

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

		is_bridge[i] = false;
	}

	int num_bridge = 0;

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

		from = edge[i].s;
		to = edge[i].t;

		if(order[edge[i].s] > order[edge[i].t]){
			swap(from,to);
		}

		if(order[from] < lowlink[to]){
			num_bridge++;
			is_bridge[i] = true;
		}
	}

	if(num_bridge == 0){
		printf("%d %d\n",ans_from+1,ans_to+1);
		return 0;
	}

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

		parent[i] = i;
		height[i] = 0;
	}

	//union-findでグループ分け
	for(int i = 0; i < E; i++){
		if(is_bridge[i]){
			continue;
		}

		unite(edge[i].s,edge[i].t);
	}

	//グループごとの重さを調べる
	for(int i = 0; i < V; i++){

		sum_weight[i] = 0;
	}
	for(int i = 0; i < E; i++){
		if(isSame(edge[i].s,edge[i].t)){
			sum_weight[find(edge[i].s)] += edge[i].cost;
		}
	}

	//グループ間のエッジを張る(すべて橋のはず)
	for(int i = 0; i < E; i++){

		if(isSame(edge[i].s,edge[i].t)){
			continue;
		}

		Group_G[find(edge[i].s)].push_back(Info(find(edge[i].t),i));
		Group_G[find(edge[i].t)].push_back(Info(find(edge[i].s),i));
	}

	int root;
	//木にする
	for(int i = 0; i < V; i++){
		if(i == find(i)){
			root = i;
			dfs(i,-1);
			break;
		}
	}

	for(int i = 0; i < V; i++){
		if(i != find(i) ||i == root)continue;

		if(minimum > abs(sum-(nodes[i].W+nodes[i].edge_cost)-nodes[i].W)){

			minimum = abs(sum-(nodes[i].W+nodes[i].edge_cost)-nodes[i].W);
			ans_from = min(i,nodes[i].parent);
			ans_to = max(i,nodes[i].parent);
		}else if(minimum == abs(sum-(nodes[i].W+nodes[i].edge_cost)-nodes[i].W)){

			if(ans_from > min(i,nodes[i].parent)){

				ans_from = min(i,nodes[i].parent);
				ans_to = max(i,nodes[i].parent);

			}else if(ans_from == min(i,nodes[i].parent) && ans_to > max(i,nodes[i].parent)){

				ans_to = max(i,nodes[i].parent);
			}
		}
	}

	printf("%d %d\n",ans_from+1,ans_to+1);

	return 0;
}
accuracy: 0.6216216216216216
time_ms: 60
memory_kb: 21640
final_rank: 15

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define NUM 100005

struct Edge{
	Edge(){
		s = t = 0;
		cost = 0;
	}
	Edge(int arg_s,int arg_t,ll arg_cost){
		s = arg_s;
		t = arg_t;
		cost = arg_cost;
	}
	bool operator<(const struct Edge &arg) const{
		if(s != arg.s){
			return s < arg.s;
		}else{
			return t < arg.t;
		}
	}
	int s,t;
	ll cost;
};

struct Info{
	Info(int arg_to,int arg_edge_id){
		to = arg_to;
		edge_id = arg_edge_id;
	}
	int to,edge_id;
};

struct Node{

	int parent;
	ll W,edge_cost;
};

int V,E,number;
int order[NUM],lowlink[NUM];
int height[NUM],parent[NUM];
ll sum_weight[NUM];
bool visited[NUM],is_bridge[NUM];
Edge edge[NUM];
Node nodes[NUM];
vector<Info> G[NUM],Group_G[NUM];

int find(int x){
	if(x == parent[x])return x;
	else{
		return parent[x] = find(parent[x]);
	}
}

bool isSame(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(height[x] < height[y]){
		parent[x] = y;
	}else{
		parent[y] = x;
		if(height[x] == height[y])height[x]++;
	}
}

void recursive(int node_id){

	order[node_id] = number++;
	lowlink[node_id] = order[node_id];

	int next_node;

	for(int i = 0; i < G[node_id].size(); i++){
		next_node = G[node_id][i].to;

		if(order[next_node] == -1){ //頂点next_nodeをまだ訪れていない

			visited[G[node_id][i].edge_id] = true;
			recursive(next_node);
			lowlink[node_id] = min(lowlink[node_id],lowlink[next_node]);

		}else if(visited[G[node_id][i].edge_id] == false){ //後退辺をまだ通っていない場合
			lowlink[node_id] = min(lowlink[node_id],order[next_node]);
		}
	}
}

//部分木の総コストを計算
ll dfs(int group_id,int parent){

	ll ret = sum_weight[group_id];

	int debug,apple;

	for(int i = 0; i < Group_G[group_id].size(); i++){
		if(Group_G[group_id][i].to == parent){
			nodes[group_id].parent = edge[Group_G[group_id][i].edge_id].s;
			nodes[group_id].edge_cost = edge[Group_G[group_id][i].edge_id].cost;
			apple = edge[Group_G[group_id][i].edge_id].cost;
			continue;
		}
		ret += dfs(Group_G[group_id][i].to,group_id)+edge[Group_G[group_id][i].edge_id].cost;
	}

	nodes[group_id].W = ret; //部分木の総コスト

	return ret;
}

int main(){

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

	ll sum = 0;

	for(int i = 0; i < E; i++){
		scanf("%d %d %lld",&edge[i].s,&edge[i].t,&edge[i].cost);

		edge[i].s--;
		edge[i].t--;

		sum += edge[i].cost;

		if(edge[i].s > edge[i].t){
			swap(edge[i].s,edge[i].t);
		}
		G[edge[i].s].push_back(Info(edge[i].t,i));
		G[edge[i].t].push_back(Info(edge[i].s,i));
	}

	ll minimum = sum;
	int ans_from,ans_to;

	//1つの辺を削除する場合を調べる
	for(int i = 0; i < E; i++){

		if(minimum > sum-edge[i].cost){
			minimum = sum-edge[i].cost;
			ans_from = edge[i].s;
			ans_to = edge[i].t;
		}
	}

	sort(edge,edge+E);

	number = 0;

	for(int i = 0; i < V; i++){
		order[i] = -1;
		lowlink[i] = -1;
	}

	//★dfsで通らなかった辺が後退辺!!★
	for(int i = 0; i < E; i++){
		visited[i] = false;
	}

	recursive(0);

	int from,to;

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

		is_bridge[i] = false;
	}

	int num_bridge = 0;

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

		from = edge[i].s;
		to = edge[i].t;

		if(order[edge[i].s] > order[edge[i].t]){
			swap(from,to);
		}

		if(order[from] < lowlink[to]){
			num_bridge++;
			is_bridge[i] = true;
		}
	}

	if(num_bridge == 0){
		printf("%d %d\n",ans_from+1,ans_to+1);
		return 0;
	}

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

		parent[i] = i;
		height[i] = 0;
	}

	//union-findでグループ分け
	for(int i = 0; i < E; i++){
		if(is_bridge[i]){
			continue;
		}

		unite(edge[i].s,edge[i].t);
	}

	//グループごとの重さを調べる
	for(int i = 0; i < V; i++){

		sum_weight[i] = 0;
	}
	for(int i = 0; i < E; i++){
		if(isSame(edge[i].s,edge[i].t)){
			sum_weight[find(edge[i].s)] += edge[i].cost;
		}
	}

	//グループ間のエッジを張る(すべて橋のはず)
	for(int i = 0; i < E; i++){

		if(isSame(edge[i].s,edge[i].t)){
			continue;
		}

		Group_G[find(edge[i].s)].push_back(Info(find(edge[i].t),i));
		Group_G[find(edge[i].t)].push_back(Info(find(edge[i].s),i));
	}

	int root;
	//木にする
	for(int i = 0; i < V; i++){
		if(i == find(i)){
			root = i;
			dfs(i,-1);
			break;
		}
	}

	for(int i = 0; i < V; i++){
		if(i != find(i) ||i == root)continue;

		if(minimum > abs(sum-(nodes[i].W+nodes[i].edge_cost)-nodes[i].W)){

			minimum = abs(sum-(nodes[i].W+nodes[i].edge_cost)-nodes[i].W);
			ans_from = min(i,nodes[i].parent);
			ans_to = max(i,nodes[i].parent);
		}else if(minimum == abs(sum-(nodes[i].W+nodes[i].edge_cost)-nodes[i].W)){

			if(ans_from > min(i,nodes[i].parent)){

				ans_from = min(i,nodes[i].parent);
				ans_to = max(i,nodes[i].parent);

			}else if(ans_from == min(i,nodes[i].parent) && ans_to > max(i,nodes[i].parent)){

				ans_to = max(i,nodes[i].parent);
			}
		}
	}

	printf("%d %d\n",ans_from+1,ans_to+1);

	return 0;
}
accuracy: 0.35135135135135137
time_ms: 30
memory_kb: 16812
final_rank: 16

code_snippet: /*  -*- coding: utf-8 -*-
 *
 * 2893.cc: Balanced Edge Deletion
 */

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<deque>
#include<algorithm>
#include<numeric>
#include<utility>
#include<complex>
#include<functional>
 
using namespace std;

/* constant */

const int MAX_N = 100000;
const int MAX_GN = 100000;
const int MAX_M = 100000;
const long long LINF = 1LL << 60;

/* typedef */

typedef long long ll;
typedef queue<int> qi;
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef vector<bool> vb;
typedef vector<vi> vvi;
typedef vector<pii> vpii;
typedef stack<int> si;

struct Edge {
  int i, j, w;
  Edge() {}
  Edge(int _i, int _j, int _w): i(_i), j(_j), w(_w) {}
};

/* global variables */

vpii nbrs[MAX_N], gnbrs[MAX_GN];
Edge es[MAX_M];
int gmap[MAX_N], gps[MAX_GN], gpws[MAX_GN], gcns[MAX_GN];
ll gws[MAX_GN];

/* subroutines */

void bcc_visit(const vpii *nbrs, int v, int u,
	       vpii& brdg, vvi& tecomp,
	       si& roots, si& S, vb& inS, vi& num, int& time) {
  num[v] = ++time;
  S.push(v); inS[v] = true;
  roots.push(v);

  const vpii& nbrv = nbrs[v];
  for (vpii::const_iterator vit = nbrv.begin(); vit != nbrv.end(); vit++) {
    const int& w = vit->first;
    if (num[w] == 0)
      bcc_visit(nbrs, w, v, brdg, tecomp, roots, S, inS, num, time);
    else if (u != w && inS[w])
      while (num[roots.top()] > num[w]) roots.pop();
  }

  if (v == roots.top()) {
    brdg.push_back(pii(u, v));
    tecomp.push_back(vi());
    for (;;) {
      int w = S.top(); S.pop(); inS[w] = false;
      tecomp.back().push_back(w);
      if (v == w) break;
    }
    roots.pop();
  }
}

void calc_bcc(const int n, const vpii *nbrs, vpii& brdg, vvi& tecomp) {
  vi num(n);
  vb inS(n);
  si roots, S;
  int time = 0;

  for (int u = 0; u < n; u++)
    if (num[u] == 0) {
      bcc_visit(nbrs, u, n, brdg, tecomp, roots, S, inS, num, time);
      brdg.pop_back();
    }
}

inline ll absll(ll a) { return (a >= 0) ? a : -a; }

/* main */

int main() {
  int n, m;
  scanf("%d%d", &n, &m);

  ll wsum = 0;
  for (int i = 0; i < m; i++) {
    int u, v, w;
    scanf("%d%d%d", &u, &v, &w);
    u--, v--;
    nbrs[u].push_back(pii(v, i));
    nbrs[v].push_back(pii(u, i));
    es[i] = Edge(u, v, w);
    wsum += w;
  }

  vpii brdg;
  vvi tecomp;
  calc_bcc(n, nbrs, brdg, tecomp);
  //printf("brdg=%d\n", (int)brdg.size());

  int gn = tecomp.size();
  for (int i = 0; i < gn; i++)
    for (int j = 0; j < tecomp[i].size(); j++)
      gmap[tecomp[i][j]] = i;
  //for (int i = 0; i < n; i++) printf("%d ", gmap[i]); putchar('\n');

  for (int i = 0; i < m; i++) {
    Edge &ei = es[i];
    int &gi = gmap[ei.i], &gj = gmap[ei.j];

    if (gi == gj) gws[gi] += ei.w;
    else {
      gnbrs[gi].push_back(pii(gj, ei.w));
      gnbrs[gj].push_back(pii(gi, ei.w));
    }
  }

  gps[0] = -1, gpws[0] = 0;
  qi q;
  q.push(0);

  while (! q.empty()) {
    int u = q.front(); q.pop();
    int &up = gps[u];
    vpii &gnbru = gnbrs[u];
    for (vpii::iterator vit = gnbru.begin(); vit != gnbru.end(); vit++) {
      int &v = vit->first;
      if (v != up) {
	gps[v] = u, gpws[v] = vit->second;
	gcns[u]++;
	q.push(v);
      }
    }
  }

  for (int i = 0; i < gn; i++)
    if (gcns[i] == 0) q.push(i);

  while (! q.empty()) {
    int u = q.front(); q.pop();
    int &up = gps[u];
    if (up >= 0) {
      gws[up] += gws[u] + gpws[u];
      if (--gcns[up] == 0) q.push(up);
    }
  }

  ll mind = LINF;
  int mini = -1, minj = -1;

  for (int i = 0; i < m; i++) {
    Edge &ei = es[i];
    int &gi = gmap[ei.i], &gj = gmap[ei.j];

    ll d;
    if (gi == gj) 
      d = wsum - ei.w;
    else {
      if (gps[gi] == gj)
	d = absll((wsum - gws[gi] - gpws[gi]) - gws[gi]);
      else
	d = absll((wsum - gws[gj] - gpws[gj]) - gws[gj]);
    }

    if (mind > d ||
	(mind == d && (mini > ei.i || (mini == ei.j && minj > ei.j))))
      mind = d, mini = ei.i, minj = ei.j;
  }

  printf("%d %d\n", mini + 1, minj + 1);
  return 0;
}
accuracy: 1
time_ms: 70
memory_kb: 25064
final_rank: 5