P4180 嚴格次小生成樹[BJWC2010]
題目鏈接
https://www.luogu.org/problemnew/show/P4180
題目描述
小C最近學了很多最小生成樹的算法,Prim算法、Kurskal算法、消圈算法等等。正當小C洋洋得意之時,小P又來潑小C冷水了。小P說,讓小C求出一個無向圖的次小生成樹,而且這個次小生成樹還得是嚴格次小的,也就是說:如果最小生成樹選擇的邊集是EM,嚴格次小生成樹選擇的邊集是ES,那麽需要滿足:(value(e)表示邊e的權值) \sum_{e \in E_M}value(e)<\sum_{e \in E_S}value(e)∑e∈EM??value(e)<∑e∈ES??value(e)
這下小 C 蒙了,他找到了你,希望你幫他解決這個問題。
輸入輸出格式
輸入格式:
第一行包含兩個整數N 和M,表示無向圖的點數與邊數。 接下來 M行,每行 3個數x y z 表示,點 x 和點y之間有一條邊,邊的權值為z。
輸出格式:
包含一行,僅一個數,表示嚴格次小生成樹的邊權和。(數據保證必定存在嚴格次小生成樹)
輸入輸出樣例
輸入樣例#1:5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
輸出樣例#1: 11
說明
數據中無向圖無自環; 50% 的數據N≤2 000 M≤3 000; 80% 的數據N≤50 000 M≤100 000; 100% 的數據N≤100 000 M≤300 000 ,邊權值非負且不超過 10^9 。
題目分析
所謂嚴格的次小是指權值嚴格大於最小生成樹的次小生成樹,我們知道一般次小生成樹,只需要先用kruskal算法求得最小生成樹,然後暴力枚舉非樹邊,替換路徑最大邊即可。
這題也可以類似思考,但有一個問題,如果最大邊與當前枚舉邊相等時,我們不能替換,於是求其次用次小邊來替換。這樣我們需要求得路徑上的最小邊和次小邊(小於最小邊),於是我們可以利用LCA的倍增算法來維護。
預處理過程需要考慮i -> f[i][j]與f[i][j] -> f[f[i][j]][j]這兩段的合並,考慮這兩段的最大值相同與不同情況,相同則說明次大值是這兩個的次大值的最大值,不同的話,假設(a,b),(c,d)表示兩段的(最大,次大),若a > c,顯然次大為max(b, c), c > a的情況類似,見代碼中的函數ck1。
預處理完,維護沿單鏈向上跳,記單鏈的(最大,次大)為(a, b),當前得到最優值(lx, ln),分三種情況討論,lx與a的大小關系,見代碼中的函數ck3。
參考代碼
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 6 using namespace std; 7 //#define DEBUG(x) cerr << #x << "=" << x << endl 8 #define Maxn 300010 9 10 struct edge 11 { 12 int to; 13 int w; 14 int next; 15 }p[Maxn]; 16 17 int head[Maxn / 3], tot; 18 19 void addedge(int a, int b, int c) 20 { 21 p[tot].to = b; 22 p[tot].w = c; 23 p[tot].next = head[a]; 24 head[a] = tot++; 25 } 26 27 struct line 28 { 29 int u, v, w; 30 bool operator<(const line &a)const 31 { 32 return w < a.w; 33 } 34 }q[Maxn]; 35 36 int vis[Maxn]; 37 int fa[Maxn / 3]; 38 39 int findset(int x) 40 { 41 return fa[x] == x ? x : (fa[x] = findset(fa[x])); 42 } 43 44 int unionset(int a, int b) 45 { 46 return fa[findset(a)] = findset(b); 47 } 48 int dep[Maxn / 3]; 49 int f[Maxn / 3][20], g[Maxn / 3][20], h[Maxn / 3][20]; 50 51 void dfs(int u, int fa) 52 { 53 f[u][0] = fa; 54 dep[u] = dep[fa] + 1; 55 for (int i = head[u]; i != -1; i = p[i].next) 56 { 57 int v = p[i].to; 58 if(v != fa) 59 { 60 g[v][0] = p[i].w; 61 h[v][0] = -1; 62 dfs(v, u); 63 } 64 } 65 } 66 67 void ck1(int &a, int &b, int c, int d, int e, int f) 68 { 69 if (c == e) 70 { 71 a = c; 72 b = max(d, f); 73 return; 74 } 75 if (c > e) 76 { 77 swap(c, e); 78 swap(d, f); 79 } 80 a = e; 81 b = max(c, f); 82 } 83 84 int ck2(int lx, int ln, int w) 85 { 86 if (w == lx) return w-ln; 87 return w-lx; 88 } 89 90 void ck3(int &lx, int &ln, int u, int t) 91 { 92 if (g[u][t] == lx) 93 ln = max(ln, h[u][t]); 94 else if(g[u][t] < lx) 95 ln = max(ln, g[u][t]); 96 else 97 { 98 ln = (lx, h[u][t]); 99 lx = g[u][t]; 100 } 101 } 102 void init(int n) 103 { 104 dfs(1, 0); 105 for (int j = 0; j < 18; j++) 106 for (int i = 1; i <= n; i++) 107 { 108 if (!f[i][j]) f[i][j + 1] = 0; 109 else 110 { 111 f[i][j + 1] = f[f[i][j]][j]; 112 ck1(g[i][j+1], h[i][j+1], g[i][j], h[i][j], g[f[i][j]][j], h[f[i][j]][j]); 113 } 114 } 115 } 116 117 int LCA(int u, int v, int w) 118 { 119 int lx = -1, ln = -1; 120 if (dep[u] < dep[v]) swap(u,v); 121 int df = dep[u] - dep[v], t = 0; 122 while(df) 123 { 124 if(df&1) 125 { 126 ck3(lx, ln, u, t); 127 u = f[u][t]; 128 } 129 t++; 130 df>>=1; 131 } 132 if(u == v) return ck2(lx, ln, w); 133 for(int i = 18; i >= 0; i--) 134 { 135 if(f[u][i] != f[v][i]) 136 { 137 ck3(lx, ln, u, i); 138 ck3(lx, ln, v, i); 139 u=f[u][i]; 140 v=f[v][i]; 141 } 142 } 143 ck3(lx, ln, u, 0); 144 ck3(lx, ln, v, 0); 145 return ck2(lx, ln, w); 146 } 147 int main() 148 { 149 freopen("tree.in", "r", stdin); 150 freopen("tree.out", "w", stdout); 151 int n, m; 152 scanf("%d%d", &n, &m); 153 for (int i = 0; i < m; i++) 154 scanf("%d%d%d", &q[i].u, &q[i].v, &q[i].w); 155 sort(q, q + m); 156 for (int i = 1; i <= n; i++) fa[i] = i; 157 memset(head, -1, sizeof(head)); 158 memset(vis, 0, sizeof(vis)); 159 tot = 0; 160 int cnt = 0; 161 long long ans = 0; 162 for (int i = 0; i < m; i++) 163 { 164 int u = q[i].u, v = q[i].v; 165 if (findset(u) == findset(v)) continue; 166 unionset(u, v); 167 vis[i] = 1; 168 addedge(u, v, q[i].w); 169 addedge(v, u, q[i].w); 170 ans += q[i].w; 171 if (++cnt==n-1) break; 172 } 173 init(n); 174 int z = 0x3f3f3f3f; 175 for (int i = 0; i < m; i++) 176 if (!vis[i]) 177 z = min(z, LCA(q[i].u, q[i].v, q[i].w)); 178 printf("%lld\n", ans + z); 179 return 0; 180 }
後記
本來想著在這篇博客的後面講解一下倍增LCA和求最小生成樹的兩種方法
但是想了想,這樣的話未免太亂了些
於是決定再開新博客來講解
qwq
P4180 嚴格次小生成樹[BJWC2010]