1. 程式人生 > 實用技巧 >AcWing 356. 次小生成樹

AcWing 356. 次小生成樹

AcWing 356. 次小生成樹

一道蠻經典的題目,隨手記錄下。

原題連結:https://www.acwing.com/problem/content/description/358/

題目描述:

給定一張 N 個點 M 條邊的無向圖,求無向圖的嚴格次小生成樹。

設最小生成樹的邊權之和為sum,嚴格次小生成樹就是指邊權之和大於sum的生成樹中最小的一個。

注意本題所求的次小生成樹是嚴格次小的

題解:先用kruskal求出給定圖的最小生成樹,再列舉沒有被選上的邊,假設把這條邊加上,就會形成一個環,只要把環中的最大邊刪去,就可以得到一顆新的樹,列舉後得到最小的新樹就是次小生成樹。這裡注意

因為是嚴格次小所以我們需要判斷每個環中的最大邊是否和新加入的邊一樣,所以還需要維護一個次大邊。我們用lca倍增來維護就可以使時間複雜度降到nlogn

#include <stdio.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map> 
#include <stack>
#include <sstream>
#include <set>
#pragma GCC optimize(2)

//#define int long long
#define rush() int T;scanf("%d",&T);for(int Ti=1;Ti<=T;++Ti)
#define
IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); #define mm(i,v) memset(i,v,sizeof i); #define mp(a, b) make_pair(a, b) #define pi acos(-1) #define fi first #define se second //你冷靜一點,確認思路再敲!!! using namespace std; typedef long long ll; typedef pair<int, int > PII; priority_queue< PII, vector<PII>, greater<PII> > que; stringstream ssin;
// ssin << string while ( ssin >> int) const int N = 1e5 + 5, M = 6e5 + 5, mod = 1e9 + 7, INF = 0x3f3f3f3f; int n, m, t, anc; int e[M], ne[M], h[M], w[M], idx; int fa[N][20], d1[N][20], d2[N][20], depth[N]; int p[N]; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } void add(int a, int b, int c) { e[idx] = b; w[idx] = c; ne[idx] = h[a]; h[a] = idx++; } struct Edge { int a, b, w; bool vis; bool operator< (const Edge &t) { return w < t.w; } }edge[M]; int find(int x) { if (p[x] != x) return p[x] = find(p[x]); else return p[x]; } ll kruskal() { ll res = 0; for (int i = 1; i <= n; ++i) p[i] = i; sort(edge + 1, edge + 1 + m); for (int i = 1; i <= m; ++i) { int a = edge[i].a, b = edge[i].b, c = edge[i].w; int pa = find(a), pb = find(b); if (pa == pb) continue; p[pa] = pb; edge[i].vis = 1; res += c; } return res; } void build() { mm(h, -1); for (int i = 1; i <= m; ++i) { if (!edge[i].vis) continue; int a = edge[i].a, b = edge[i].b, c = edge[i].w; add(a, b, c); add(b, a, c); } } void bfs() { mm(depth, 0x3f); queue<int>q; q.push(1); depth[0] = 0; depth[1] = 1; while (!q.empty()) { int u = q.front(); q.pop(); for (int i = h[u]; ~i; i = ne[i]) { int v = e[i], z = w[i]; if (depth[v] > depth[u] + 1) { depth[v] = depth[u] + 1; fa[v][0] = u; d1[v][0] = z; d2[v][0] = -INF; q.push(v); for (int k = 1; k <= t; ++k) { anc = fa[v][k - 1]; fa[v][k] = fa[anc][k - 1]; int dis[4] = {d1[v][k - 1], d1[anc][k - 1], d2[v][k - 1], d2[anc][k - 1]}; d1[v][k] = d2[v][k] = -INF; for (int j = 0; j < 4; ++j) { if (dis[j] > d1[v][k]) { d2[v][k] = d1[v][k]; d1[v][k] = dis[j]; } else if (dis[j] != d1[v][k]){ d2[v][k] = max(d2[v][k], dis[j]); } } } } } } } int lca(int a, int b, int c) { int dd[N + N], cnt = 0; if (depth[a] < depth[b]) swap(a, b); for (int k = t; k >= 0; --k) { if (depth[fa[a][k]] >= depth[b]) { dd[++cnt] = d1[a][k]; dd[++cnt] = d2[a][k]; a = fa[a][k]; } } if (a != b) { for (int k = t; k >= 0; --k) { if (fa[a][k] != fa[b][k]) { dd[++cnt] = d1[a][k]; dd[++cnt] = d2[a][k]; dd[++cnt] = d1[b][k]; dd[++cnt] = d2[b][k]; a = fa[a][k]; b = fa[b][k]; } } dd[++cnt] = d1[a][0]; dd[++cnt] = d1[b][0]; } int dis1 = -INF, dis2 = -INF; // 最大和次大 for (int i = 1; i <= cnt; ++i) { int d = dd[i]; if (d > dis1) { dis2 = dis1; dis1 = d; } else if (d != dis1) { dis2 = max(dis2, d); } } if (c > dis1) return c - dis1; if (c > dis2) return c - dis2; return INF; } int main() { // freopen("in.txt","r",stdin); //輸入重定向,輸入資料將從in.txt檔案中讀取 // freopen("out.txt","w",stdout); //輸出重定向,輸出資料將儲存在out.txt檔案中 n = read(); m = read(); t = log2(N) + 1; for (int i = 1; i <= m; ++i) { int a, b, c; a = read(); b = read(); c = read(); edge[i] = {a, b, c}; } ll res = kruskal(); // cout << res << '\n'; build(); bfs(); ll ans = 1e18; for (int i = 1; i <= m; ++i) { if (edge[i].vis) continue; int a = edge[i].a, b = edge[i].b, c = edge[i].w; ans = min(ans, res + lca(a, b, c)); } cout << ans << '\n'; // system("pause"); }