Codeforces 700 C. Break Up(Tarjan求橋)
阿新 • • 發佈:2018-08-29
void eset fprintf air read emc endif link lca 點的父親時候跳過,後面都需要更新
題意
給你一個有 \(n\) 個點, \(m\) 條邊的無向圖,每條有邊權 \(w_i\) ,現在要選擇至多兩條邊斷開,使得 \(S, T\) 不連通,並且使得邊權和盡量小。
\(n \le 1000, m \le 30000\)
題解
我們分要選的邊數進行考慮。
- \(0\) 條邊:一開始 \(S,T\) 不連通直接判掉即可。
- \(1\) 條邊:我們發現數據較小,可以暴力做。首先這條邊必定存在於 \(S,T\) 在
Dfs
樹的路徑上,一開始先 Dfs 求出路徑,然後依次枚舉每條邊斷開,再用Dfs
判是否連通就行了,最後把邊權取個 \(\min\) 就行了。復雜度是 \(O(n(n+m))\) 。 - \(2\)
Dfs
樹,然後用Tarjan
求出橋邊就行了,然後依次判斷這些樹邊是否為橋邊。如果是,那麽就是一個合法解,最後把答案和這兩條邊權和取 \(\min\) 。復雜度也是 \(O(n(n+m))\) 的。
代碼
這裏需要註意實現細節。比如 Dfs
求 \(S \to T\) 路徑,我們可以不要求 \(Lca\) ,可以從 \(S\) Dfs
的時候,記下這條路徑是否到達了 \(T\) ,因為是樹所以路徑唯一。
然後會有重邊的情況,我們可以第一次枚舉到 \(u\)
lowlink
就行了。
#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << x << endl #define DEBUG(...) fprintf(stderr, __VA_ARGS__) #define mp make_pair using namespace std; typedef pair<int, int> PII; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48); return x * fh; } void File() { #ifdef zjp_shadow freopen ("F.in", "r", stdin); freopen ("F.out", "w", stdout); #endif } const int N = 1010, M = 30100 << 1, inf = 0x7f7f7f7f; int Head[N], Next[M], to[M], val[M], e = 1; inline void add_edge(int u, int v) { to[++ e] = v; Next[e] = Head[u]; Head[u] = e; } inline void Add(int u, int v) { add_edge(u, v); add_edge(v, u); } #define Travel(i, u, v) for(int i = Head[u], v = to[i]; i; v = to[i = Next[i]]) int S, T; vector<int> V, G; bitset<N> vis; bitset<M> ban; bool Dfs(int u) { vis[u] = true; if (u == T) return true; Travel(i, u, v) if (!ban[i] && !vis[v] && Dfs(v)) return V.push_back(i), true; return false; } int clk, dfn[N], lowlink[N]; bitset<M> Bridge; void Tarjan(int u, int fa = 0) { dfn[u] = lowlink[u] = ++ clk; bool fir = true; Travel(i, u, v) if (!ban[i]) { if (v == fa && fir) { fir = false; continue ; } if (!dfn[v]) { Tarjan(v, u); chkmin(lowlink[u], lowlink[v]); if (lowlink[v] > dfn[u]) Bridge[i] = Bridge[i ^ 1] = true; } else chkmin(lowlink[u], dfn[v]); } } PII Ans; int main () { File(); int n = read(), m = read(); S = read(); T = read(); For (i, 1, m) { int u = read(), v = read(), w = read(); Add(u, v); val[e] = val[e ^ 1] = w; } if (!Dfs(S)) return puts("0\n0"), 0; G.swap(V); int ans = inf; for (int cur : G) { ban[cur] = ban[cur ^ 1] = true; vis.reset(); if (!Dfs(S)) { if (chkmin(ans, val[cur])) Ans = mp(cur >> 1, 0); ban[cur] = ban[cur ^ 1] = false; continue ; } clk = 0; Bridge.reset(); Set(dfn, 0); For (i, 1, n) if (!dfn[i]) Tarjan(i, 0); for (int cut : V) if (Bridge[cut] && chkmin(ans, val[cur] + val[cut])) Ans = mp(cur >> 1, cut >> 1); V.clear(); ban[cur] = ban[cur ^ 1] = false; } if (ans == inf) return puts("-1"), 0; printf ("%d\n", ans); if (!Ans.second) printf ("1\n%d\n", Ans.first); else printf ("2\n%d %d\n", Ans.first, Ans.second); return 0; }
Codeforces 700 C. Break Up(Tarjan求橋)