題目描述
給定一張 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之前存在一條邊,邊...