1. 程式人生 > >P4180 嚴格次小生成樹[BJWC2010]

P4180 嚴格次小生成樹[BJWC2010]

當前 space 最大 new sca 選擇 暴力枚舉 嚴格 fine

題目鏈接

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)eEM??value(e)<eES??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]