求边权最大值与最小值的差值最小的生成树,输出这个差值大小。
按权值排序,我们等同于枚举最大值,然后更新生成树让生成树的最小值尽可能最大。
也就是每次加入边,若构成环,则去掉环上最小值。
若加入边不会构成环,则此时的差值 g a p gap gap,一定是最优的 g a p gap gap。
若加入当前最大值的边会构成环,则去掉环上最小值后,更新当前的生成树中的值。重新计算 g a p gap gap,只有 g a p gap gap 更小的时候,才更新 b e s t g a p bestgap bestgap。
几个小细节:
1.由于我们要维护最小值,所以需要初始化为无穷大 i n f inf inf。
2.由于该题联通性不会变小(就是即使有 c u t cut cut 操作,也不会使得两个点失去联通性),所以可以使用并查集来判断是否成环,可以比 L C T LCT LCT 的换根再判断是否根相同节约时间。
3.倒过来的操作也是同理的,比如枚举最小值,更新最大值。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
#define Ri register int
typedef long long LL;
const int maxn = 3e5 + 5;
const int inf = 1e9;
struct node{
int fa, son[2], val, res, id; // res is the xor result
bool tag; // reverse lazy tag
}spl[maxn];
#define ls(x) (spl[x].son[0])
#define rs(x) (spl[x].son[1])
#define fa(x) (spl[x].fa)
#define ident(x, fa) (rs(fa) == x)
#define connect(x, f, no) (spl[fa(x) = f].son[no] = x)
#define ntroot(x) (ls(fa(x)) == x || rs(fa(x)) == x) // not the root of splay
#define reverse(x) swap(ls(x), rs(x)), spl[x].tag ^= true
inline void update(Ri x){
Ri l = ls(x), r = rs(x);
spl[x].res = spl[x].val;
spl[x].id = x;
if(spl[x].res > spl[l].res){
spl[x].res = spl[l].res;
spl[x].id = spl[l].id;
}
if(spl[x].res > spl[r].res){
spl[x].res = spl[r].res;
spl[x].id = spl[r].id;
}
}
inline void pushdown(Ri x){
if(spl[x].tag){
if(ls(x)) reverse(ls(x));
if(rs(x)) reverse(rs(x));
spl[x].tag = false;
}
}
inline void pushall(Ri x){
if(ntroot(x)) pushall(fa(x));
pushdown(x);
}
inline void rotate(Ri x){
Ri fa = fa(x), gf = fa(fa), k = ident(x, fa);
connect(spl[x].son[k ^ 1], fa, k);
fa(x) = gf;
if(ntroot(fa)) spl[gf].son[ident(fa, gf)] = x;
connect(fa, x, k ^ 1);
update(fa), update(x);
}
inline void splaying(Ri x){ // to the root
pushall(x); // push lazy tag
while(ntroot(x)){
int fa = fa(x), gf = fa(fa);
if(ntroot(fa)) ident(fa, gf) ^ ident(x, fa) ? rotate(x) : rotate(fa);
rotate(x);
}
}
inline void access(Ri x){ // link x to the root
for(Ri y = 0; x; x = fa(y = x)){
splaying(x);
rs(x) = y; // link right son to the root of last splay
update(x);
}
}
inline void mkroot(Ri x){ // trans the root
access(x); // link, and x will the right-most of the splay (the most dep)
splaying(x); // after splaying, x will not have right son
reverse(x); // reverse the dep index
}
inline int findroot(Ri x){ // find the root of splay
access(x);
splaying(x);
while(ls(x)){
pushdown(x);
x = ls(x);
}
splaying(x); // prevent link too long
return x;
}
inline void link(Ri x, Ri y){
mkroot(x);
if(findroot(y) == x) return; // x and y are in the same splay, not legal
fa(x) = y;
}
inline void cut(Ri x, Ri y){
mkroot(x);
if(findroot(y) != x || fa(y) != x || ls(y)) return; // not legal
fa(y) = rs(x) = 0;
update(x);
}
inline void split(Ri x, Ri y){ // split the road x to y
mkroot(x);
access(y);
splaying(y);
// now y have no right son, and the left son is a road to the x
// and the res of y is the res of the road x to y
}
//---------------------------------
pair<int, int> his[maxn];
int edge, fa[maxn];
int gap, bestgap;
struct ooo{
int u, v, c;
inline bool operator <(const ooo&x) const &{
return c < x.c;
}
}all[maxn];
multiset<int> minnum;
inline int getroot(Ri x){
return x == fa[x] ? x : fa[x] = getroot(fa[x]);
}
inline void link1(const ooo &al){
edge++;
spl[edge].val = spl[edge].res = al.c;
spl[edge].id = edge;
his[edge].first = al.u;
his[edge].second = al.v;
link(al.u, edge);
link(edge, al.v);
minnum.insert(al.c);
gap = *minnum.rbegin() - *minnum.begin();
}
inline void link2(const ooo &al){
split(al.u, al.v);
Ri b = spl[al.v].id, c = spl[al.v].res;
minnum.erase(minnum.find(c));
cut(his[b].first, b);
cut(b, his[b].second);
link1(al);
}
int main(){
//IOS;
Ri n, m, x, y;
cin >> n >> m;
edge = n;
for(Ri i = 0; i <= n; i++){
spl[i].res = spl[i].val = inf;
fa[i] = i;
}
for(Ri i = 1; i <= m; i++)
cin >> all[i].u >> all[i].v >> all[i].c;
sort(all + 1, all + 1 + m);
for(Ri i = 1; i <= m; i++){
if(getroot(x = all[i].u) == getroot(y = all[i].v)){
if(x == y) continue;
link2(all[i]);
if(gap < bestgap)
bestgap = gap;
}else{
fa[fa[x]] = fa[y];
link1(all[i]);
bestgap = gap;
}
}
cout << bestgap << endl;
}