题目大意:
给你 n n n 个点 m m m 条边的有向图,每条边有边权 w w w,需要反转某些边,使图中至少有一个节点可以访问所有其他节点,这些反转的成本为所有反转边的最大权重。要求出成本的最大值最小为多少,不存在输出-1。
思路:
二分成本,设当前成本为 x x x 每次将所有 w < = x w <= x w<=x 的边变成无向边,缩点后建立新图,从拓扑序最小的点出发做一遍 dfs 判断是否都能访问即可 ,注意特判-1的情况
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, mod = 1e9 + 7;
const double eps = 1e-6;
typedef pair<int, int> PII;
using ARR = array<int, 3>;
int Size[N], id[N], dfn[N], low[N], vis[N], cnt, tamp;
vector<int> st;
int state[N];
int n, m;
void init() {
st.clear();
for (int i = 0; i < n; i++) {
dfn[i] = low[i] = vis[i] = id[i] = Size[i] = state[i] = 0;
}
tamp = cnt = 0;
}
void solve() {
cin >> n >> m;
vector<vector<PII>> G(n);
vector<ARR> edge(m);
for (int i = 0; i < m; i++) {
int a, b, c;
cin >> a >> b >> c;
--a, --b;
G[a].push_back({b, c});
edge[i] = {a, b, c};
}
auto g = G;
function<void(int)> tarjan = [&](int u) {
dfn[u] = low[u] = ++tamp;
st.push_back(u), vis[u] = 1;
for (auto [e, _] : g[u]) {
if (!dfn[e]) tarjan(e);
if (vis[e]) low[u] = min(low[u], low[e]);
}
if (dfn[u] == low[u]) {
int y;
++cnt;
do {
y = st.back(), st.pop_back();
vis[y] = 0;
id[y] = cnt;
Size[cnt]++;
} while (y != u);
}
};
auto check = [&](int x) {
g = G;
init();
for (auto [a, b, c] : edge) {
if (c <= x) g[b].push_back({a, c});
}
for (int i = 0; i < n; i++) if (!dfn[i]) tarjan(i);
function<void(int)> dfs = [&](int u) {
state[u] = 1;
for (auto [e, _] : g[u]) {
if (!state[e]) dfs(e);
}
};
for (int i = 0; i < n; i++) {
if (id[i] == cnt) {
dfs(i);
break;
}
}
for (int i = 0; i < n; i++) {
if (state[i] == 0) return false;
}
return true;
};
int l = 0, r = 1e9 + 1;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
if (l == 1e9 + 1) cout << -1 << '\n';
else cout << l << '\n';
}
signed main() {
cin.tie(nullptr)->ios_base::sync_with_stdio(false);
cout.tie(nullptr);
int T = 1;
cin >> T;
for (; T--;) solve();
}