次小生成樹(倍增 lca)

2021-10-10 16:23:16 字數 3661 閱讀 8151

題目描述

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

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

輸入格式

第一行包含兩個整數n和m。

接下來m行,每行包含三個整數x,y,z,表示點x和點y之前存在一條邊,邊的權值為z。

輸出格式

包含一行,僅乙個數,表示嚴格次小生成樹的邊權和。(資料保證必定存在嚴格次小生成樹)

資料範圍

n≤105,m≤3∗105

輸入樣例

5 61 2 1

1 3 2

2 4 3

3 5 4

3 4 3

4 5 6

輸出樣例

11

題目分析

這道題是秘密的牛奶運算的加強版,如果之前沒做過次小生成樹類似的題目的話建議先看看這道題。本題是對於這道題的做法進行了乙個倍增的優化。

解題步驟:

我們首先要求出最小生成樹,並記錄最小生成樹所包含的邊。

將這個最小生成樹單獨建成圖。

這道題的的資料範圍比較大,暴力預處理圖上任意兩點間路徑上的最大值和次大值會直接tle,因此我們需要對這個過程進行乙個優化。我們可以發現,在一棵樹上,任意兩點a和b之間的路徑即為:a到a和b最大公共祖先p的路徑+b到p的路徑。因此我們可以用求最小公共祖先的演算法(倍增法)來進行乙個優化:

depth[i] //i節點的深度

fa[i][j] //表示i節點向上跳2^j步後到達的節點

d1[i][j] //表示i節點向上跳2^j步的路徑上的最大邊權

d2[i][j] //表示i節點向上跳2^j步的路徑上的次大邊權對於如何預處理出這三個陣列,我們可以參考倍增法求lca的過程。

fa[i][j]=fa[fa[i][j-1]][j-1]; //fa[i][j]的值即為i節點向上跳2^j-1步得到的節點再向上跳2^j-1步後得到的節點。

d1[i][j]和d2[i][j]的值一定會在 這四個之數中。因為,i向上跳2j步的路徑即為:i向上跳2j-1步的路徑+fa[i][j-1]向上跳2j-1步的路徑。因此我們可以直接列舉這四個值來獲得d1[i][j]和d2[i][j]。

列舉所有的非樹邊(u,v,w),並將這條非樹邊加入到樹中。給最小生成樹加上一條邊之後,這個圖必定會形成乙個環,然後我們就可以在這個環上找出除該非樹邊之外的最大邊dmax1和次大邊dmax2。

我們可以求出並記錄u和v分別到其最近公共祖先p的路徑上產生的所有最大值和次大值。然後列舉所有這些最大值和次大值,並找出它們的最大值和次大值即為最大邊dmax1和次大邊dmax2。當w>dmax1時,我們為了得到次小生成樹,我們要讓生成樹的總邊權增加量最小,即加入非樹邊(u,v,w),刪除最大邊dmax1。

當w==dmax1時,用最大邊dmax1替換該非樹邊,得到的還是最小生成樹,因此,我們需要用次大邊來替換該非樹邊。

**如下

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define ll long long

#define ull unsigned long long

#define pii pair

#define x first

#define y second

using

namespace std;

const

int n=

1e5+

5,m=

3e5+

5,inf=

0x3f3f3f3f

;struct edge

}edge[m]

;int h[n]

,e[m]

,w[m]

,ne[m]

,idx;

int p[n]

;int depth[n]

,fa[n][17

],d1[n][17

],d2[n][17

];void

add(

int a,

int b,

int c)

//加邊函式

intfind

(int x)

//並查集

ll kruskal

(int m)

//kruskal演算法求最小生成樹

}return res;

}void

build

(int m)

//建圖

}int q[n]

;void

bfs();

d1[v]

[k]=d2[v]

[k]=

-inf;

for(

int j=

0;j<

4;j++

)//從這四個數中找最大值和次大值}}

}}}int

lca(

int a,

int b,

int w)

//求最小生成樹加入該非樹邊之後,得到的次小生成樹的值

if(a!=b)

dist[cnt++

]=d1[a][0

];//因為a和b只是跳到了p的下一層,所以還要記錄a和b到p的邊權

dist[cnt++

]=d1[b][0

];}int d1=

-inf,d2=

-inf;

for(

int i=

0;i)//列舉這個過程產生的所有值,找出最大值和次大值

if(w>d1)

return w-d1;

if(w>d2)

return w-d2;

return inf;

}int

main()

;}ll sum=

kruskal

(m);

//先求出該圖的最小生成樹

build

(m);

//建圖

bfs();

//預處理出需要的幾個陣列

ll ans=

1e18

;for

(int i=

0;i(!edge[i]

.used)

//列舉所有非樹邊

printf

("%lld\n"

,ans)

;return0;

}

次小生成樹 倍增 LCA

給乙個n個點m條邊 n 100000,m 300000 的無向圖,求它的嚴格次小生成樹。資料範圍很大,o n 2 的演算法顯然是不行的。由於最小生成樹是一棵樹,求任意兩點路徑上的最大 次大邊就成為了經典的lca問題。因此,我們得到了下面的演算法 1 把邊按權值從小到大排序,時間複雜度為o mlogm...

次小生成樹(最小生成樹演算法 倍增LCA)

次小生成樹 顯然就是除開最小生成樹外最小的乙個生成樹。非嚴格次小生成樹 權值和 最小生成樹。嚴格次小生成樹 權值和 最小生成樹 求解 每次在非最小生成樹的邊裡找一條,將這條邊加入樹,此時一定形成環,再刪去環中除該邊外最大的一條邊。依次列舉每條邊,找到加入邊 刪除邊最小的一種情況,即可求出非嚴格次小生...

lca 次小生成樹

倍增法求lca 求小資料次小生成樹 給定一張 n 個點 m 條邊的無向圖,求無向圖的嚴格次小生成樹。設最小生成樹的邊權之和為sum,嚴格次小生成樹就是指邊權之和大於sum的生成樹中最小的乙個。輸入格式 第一行包含兩個整數n和m。接下來m行,每行包含三個整數x,y,z,表示點x和點y之前存在一條邊,邊...