BZOJ #2238. Mst (最小生成樹+樹鏈剖分+線段樹)
阿新 • • 發佈:2020-09-06
BZOJ #2238. Mst (最小生成樹+樹鏈剖分+線段樹)
Description
給出一個N個點M條邊的無向帶權圖,以及Q個詢問,每次詢問在圖中刪掉一條邊後圖的最小生成樹。(各詢問間獨立,每次詢問不對之後的詢問產生影響,即被刪掉的邊在下一條詢問中依然存在)
Input
第一行兩個正整數N,M(N<=50000,M<=100000)表示原圖的頂點數和邊數。
下面M行,每行三個整數X,Y,W描述了圖的一條邊(X,Y),其邊權為W(W<=10000)。保證兩點之間至多隻有一條邊。
接著一行一個正整數Q,表示詢問數。(1<=Q<=100000)
下面Q行,每行一個詢問,詢問中包含一個正整數T,表示把編號為T的邊刪掉(邊從1到M按輸入順序編號)。
思路:
我們可以首先對圖跑一個最小生成樹演算法得到圖的MST,
如果無法的得出生成樹,那麼直接特判接下來每一個輸出都是無解。
如果有生成樹,那麼對於所有非生成樹上的邊\((v,u)\),
它可以代替最小生成樹上\(v->u\) 這個路徑上所有邊。
那麼我們可以在建立MST之後,進行樹鏈剖分,同時建立線段樹,功能需要有:
可以區間修改最小值,區間查詢最小值。
然後對於每一個非樹邊,我們可以區間更新路徑區間中的最小值,(注意這裡需要一個邊權轉點權的操作)。
那麼對於每一個邊被刪除之後的MST,分為兩種情況:
1、該邊是非樹邊,那麼答案還是最初的MST。
2、該邊是樹邊,那麼該邊應該替換為非樹邊構成的路徑覆蓋掉該邊的所有非樹邊
// 本題有一個巨坑的地方,題面說保證兩點之間至多隻有一條邊,但是資料中卻有這樣的情況,本人用assert親測。
程式碼:
#include <bits/stdc++.h> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #include <vector> #include <iomanip> #define ALL(x) (x).begin(), (x).end() #define sz(a) int(a.size()) #define all(a) a.begin(), a.end() #define rep(i,x,n) for(int i=x;i<n;i++) #define repd(i,x,n) for(int i=x;i<=n;i++) #define pii pair<int,int> #define pll pair<long long ,long long> #define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) #define MS0(X) memset((X), 0, sizeof((X))) #define MSC0(X) memset((X), '\0', sizeof((X))) #define pb push_back #define mp make_pair #define fi first #define se second #define eps 1e-6 #define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl using namespace std; typedef long long ll; ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;} ll lcm(ll a, ll b) {return a / gcd(a, b) * b;} ll powmod(ll a, ll b, ll MOD) {ll ans = 1; while (b) {if (b % 2)ans = ans * a % MOD; a = a * a % MOD; b /= 2;} return ans;} inline void getInt(int* p); const int maxn = 100000 + 10; const int inf = 0x3f3f3f3f; /*** TEMPLATE CODE * * STARTS HERE ***/ int n, m; int root; int cnt;// 編號用的變數 int top[maxn];// 所在重鏈的頂點編號 int id[maxn];//節點的新編號。 typedef pair<int, ll> pil; std::vector<pil> son[maxn]; int SZ[maxn];// 子數大小 int wson[maxn];// 重兒子 int fa[maxn];// 父節點 int dep[maxn];// 節點的深度 void dfs1(int id, int pre, int step) // 維護出sz,wson,fa,dep { dep[id] = step; fa[id] = pre; SZ[id] = 1; int maxson = -1; for (auto x : son[id]) { if (x.fi != pre) { dfs1(x.fi, id, step + 1); SZ[id] += SZ[x.fi]; if (SZ[x.fi] > maxson) { maxson = SZ[x.fi]; wson[id] = x.fi; } } } } //處理出top[],wt[],id[] void dfs2(int u, int topf) { id[u] = ++cnt; top[u] = topf; if (!wson[u]) // 沒兒子時直接結束 { return ; } dfs2(wson[u], topf); // 先處理重兒子 for (auto x : son[u]) { if (x.fi == wson[u] || x.fi == fa[u]) //只處理輕兒子 { continue; } dfs2(x.fi, x.fi); // 每個輕兒子以自己為top } } struct node { int l, r; int sum; int laze; } segment_tree[maxn << 2]; void build(int rt, int l, int r) { segment_tree[rt].l = l; segment_tree[rt].r = r; segment_tree[rt].laze = inf; segment_tree[rt].sum = inf; if (l == r) { return; } int mid = (l + r) >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); } void push_down(int rt) { if (segment_tree[rt].laze != inf) { int num = segment_tree[rt].laze; segment_tree[rt << 1].sum = min(num, segment_tree[rt << 1].sum); segment_tree[rt << 1].laze = min(num, segment_tree[rt << 1].laze); segment_tree[rt << 1 | 1].sum = min(num, segment_tree[rt << 1 | 1].sum); segment_tree[rt << 1 | 1].laze = min(num, segment_tree[rt << 1 | 1].laze); segment_tree[rt].laze = inf; } } void update(int rt, int l, int r, int w) { if (segment_tree[rt].r < l || segment_tree[rt].l > r) return ; if (l <= segment_tree[rt].l && segment_tree[rt].r <= r) { segment_tree[rt].sum = min(segment_tree[rt].sum, w); segment_tree[rt].laze = min(segment_tree[rt].laze, w); return ; } push_down(rt); int mid = (segment_tree[rt].l + segment_tree[rt].r) >> 1; if (r <= mid) { return update(rt << 1, l, r, w); } else { if (l > mid) { update(rt << 1 | 1, l, r, w); } else { update(rt << 1, l, r, w); update(rt << 1 | 1, l, r, w); } } } int query(int rt, int l, int r) { if (segment_tree[rt].r < l || segment_tree[rt].l > r) return inf; if (segment_tree[rt].l >= l && r >= segment_tree[rt].r) { return segment_tree[rt].sum; } push_down(rt); int mid = (segment_tree[rt].l + segment_tree[rt].r) >> 1; if (r <= mid) { return query(rt << 1, l, r); } else { if (l > mid) return query(rt << 1 | 1, l, r); else return min(query(rt << 1, l, r), query(rt << 1 | 1, l, r)); } } void uprange(int x, int y, int num) { while (top[x] != top[y]) { if (dep[top[x]] < dep[top[y]]) { swap(x, y); } update(1, id[top[x]], id[x], num); x = fa[top[x]]; } if (dep[x] > dep[y]) swap(x, y); update(1, id[x] + 1, id[y], num); } int qrange(int x, int y) { int ans = inf; if (top[x] != top[y]) { if (dep[top[x]] < dep[top[y]]) { swap(x, y); } ans = min(ans, query(1, id[top[x]], id[x])); x = fa[top[x]]; } else { if (dep[x] > dep[y]) swap(x, y); ans = min(ans, query(1, id[x] + 1, id[y])); } return ans; } struct edge { int f, t, val, id; bool operator < (const edge& bb) const { return val < bb.val; } } info[maxn]; int far[maxn]; int dsu_sz[maxn]; void dsu_init(int n) { repd(i, 0, n) { far[i] = i; dsu_sz[i] = 1; } } int findpar(int x) { if (x == far[x]) { return x; } else { return far[x] = findpar(far[x]); } } void mg(int x, int y) { x = findpar(x); y = findpar(y); if (x == y) return; if (dsu_sz[x] > dsu_sz[y]) { dsu_sz[x] += dsu_sz[y]; far[y] = x; } else { dsu_sz[y] += dsu_sz[x]; far[x] = y; } } bool isok = 0; int ans = 0; bool vis[maxn]; void solve() { sort(info + 1, info + 1 + m); dsu_init(n); int cnt = 0; repd(i, 1, m) { int x = findpar(info[i].f); int y = findpar(info[i].t); int w = info[i].val; if (x != y) { ans += w; cnt++; son[info[i].f].push_back(mp(info[i].t, w)); son[info[i].t].push_back(mp(info[i].f, w)); mg(x, y); } else { vis[info[i].id] = 1; } } isok = cnt == n - 1; } edge ed[maxn]; int main() { cin >> n >> m; root = 1; int u, v, val; repd(i, 1, m) { cin >> u >> v >> val; info[i] = (edge) {u, v, val, i}; ed[i] = info[i]; } solve(); if (isok == 0) { int q; cin >> q; while (q--) { int i; cin >> i; cout << "Not connected" << endl; } return 0; } dfs1(root, 0, 1); dfs2(root, root); build(1, 1, n); repd(i, 1, m) { if (!vis[i]) continue; auto e = ed[i]; uprange(e.t, e.f, e.val); } int q, op, x, y; cin >> q; while (q--) { cin >> op; x = ed[op].f; y = ed[op].t; if (vis[op]) { cout << ans << endl; continue; } int res = qrange(x, y); if (res == inf) { cout << "Not connected" << endl; } else { cout << ans - ed[op].val + res << endl; } } return 0; }