P4234 最小差值生成树
https://www.luogu.com.cn/problem/P4234
求最小差值生成树
考虑先把边从小到大排序(从大到小也可以,就是反过来而已)
然后一条条边枚举,如果两端点还未联通,直接联通
如果整个图已经联通了,此时可以理解为枚举了边的最大值(因为边权从小到大排序了),最大值确定,就应该要让最小值越大越好,又因为要在这两个端点行成的路径上删掉一个点,那么就删掉环上权值最小的点
所以按照上面的描述每次如果每联通就联通,如果联通了就删路径权值最小边并加新边就行了,用 lct 维护,因为涉及边的信息,还要用新建虚拟节点表示边
然后每次枚举时,如果整个图联通,就用当前边权减去当前全局最小值,全局最小值用带删除的堆维护的,好像有点麻烦了
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
#define N 50005
#define M 200005
int n,m;
int U[M],V[M],w[M];
int tmp[10];
struct tr{
tr *son[2],*fa;
int tag;
int valmin,min;
int valmax,max;
}*null,*pos[N+M],dizhi[N+M];
#define ident(tree,fa) (fa->son[1]==tree)
#define notroot(tree) (tree->fa->son[0]==tree||tree->fa->son[1]==tree)
inline void connect(tr *tree,tr *fa,int k){fa->son[k]=tree;tree->fa=fa;}
inline void pushdown(tr *tree){
if(!tree->tag) return;
tree->son[0]->tag^=1;tree->son[1]->tag^=1;
std::swap(tree->son[0],tree->son[1]);
tree->tag=0;
}
inline int min(int x,int y){return w[x]<w[y]?x:y;}
inline int max(int x,int y){return w[x]>w[y]?x:y;}
inline void pushup(tr *tree){
tree->min=min(tree->valmin,min(tree->son[0]->min,tree->son[1]->min));
tree->max=max(tree->valmax,max(tree->son[0]->max,tree->son[1]->max));
}
inline void rotate(tr *tree){
tr *fa=tree->fa,*faa=fa->fa;
pushdown(fa);pushdown(tree);
int k=ident(tree,fa);
connect(tree->son[k^1],fa,k);
tree->fa=faa;
if(notroot(fa)) faa->son[ident(fa,faa)]=tree;
connect(fa,tree,k^1);
pushup(fa);pushup(tree);
}
inline void splay(reg tr *tree){
reg tr *fa,*faa;
while(notroot(tree)){
fa=tree->fa;faa=fa->fa;
if(notroot(fa)) rotate(ident(tree,fa)^ident(fa,faa)?tree:fa);
rotate(tree);
}
}
inline void access(reg tr *x){
for(reg tr *lastx=null;x!=null;lastx=x,x=x->fa){
pushdown(x);
splay(x);
x->son[1]=lastx;pushup(x);
}
}
inline void makeroot(tr *x){
access(x);
splay(x);
x->tag^=1;
}
inline tr *findroot(tr *x){
access(x);splay(x);
pushdown(x);
while(x->son[0]!=null) x=x->son[0],pushdown(x);
splay(x);
return x;
}
inline int linked(tr *x,tr *y){
makeroot(x);
return findroot(y)==x;
}
inline void split(tr *x,tr *y){
makeroot(x);
access(y);splay(y);
}
inline void link(tr *x,tr *y){
makeroot(x);
if(findroot(y)!=x) x->fa=y;
}
inline void cut(tr *x,tr *y){
split(x,y);
x->fa=y->son[0]=null;
}
inline void init(){
null=&dizhi[0];
dizhi[0].max=dizhi[0].valmax=m+1;
for(reg int i=1;i<=n;i++){
pos[i]=&dizhi[i];
dizhi[i].son[0]=dizhi[i].son[1]=dizhi[i].fa=null;
dizhi[i].max=dizhi[i].valmax=m+1;
}
w[0]=1e9;w[m+1]=-1e9;
}
inline void creat(int i){
pos[i]=&dizhi[i];
dizhi[i].son[0]=dizhi[i].son[1]=dizhi[i].fa=null;
dizhi[i].min=dizhi[i].valmin=i-n;
dizhi[i].max=dizhi[i].valmax=i-n;
}
struct edges{
int u,v,w;
}edge[M];
inline int cmp_edge(edges a,edges b){return a.w<b.w;}
struct HEAP{
std::priority_queue<int>ins,del;
inline void delete_(int num){del.push(num);}
inline void insert(int num){ins.push(num);}
inline int top(){
while(!del.empty()&&!ins.empty()&&del.top()==ins.top()) del.pop(),ins.pop();
return ins.top();
}
}heap;
int main(){
n=read();m=read();
init();
for(reg int i=1;i<=m;i++) edge[i].u=read(),edge[i].v=read(),edge[i].w=read();
std::sort(edge+1,edge+1+m,cmp_edge);
int u,v,ans=1e9;
for(reg int i=1,cnt=1;i<=m;i++){
u=U[i]=edge[i].u;v=V[i]=edge[i].v;w[i]=edge[i].w;
creat(i+n);
if(u==v) continue;
if(!linked(pos[u],pos[v])){
link(pos[u],pos[i+n]),link(pos[v],pos[i+n]);cnt++;
heap.insert(-w[i]);
}
else{
split(pos[u],pos[v]);
int tmp=pos[v]->min;
cut(pos[U[tmp]],pos[tmp+n]);cut(pos[V[tmp]],pos[tmp+n]);
link(pos[u],pos[i+n]);link(pos[v],pos[i+n]);
heap.delete_(-w[tmp]);heap.insert(-w[i]);
}
if(cnt==n) ans=std::min(ans,w[i]+heap.top());
}
printf("%d",ans);
return 0;
}