洛谷4234最小差值生成树题解(最小生成树+LCT)

题目:luogu4234.
题目大意:给定一张 n n n个点 m m m条边无向图,求这张无向图最大边和最小边的最小权值差.
1 ≤ n ≤ 5 ∗ 1 0 4 , 1 ≤ m ≤ 2 ∗ 1 0 5 1\leq n\leq 5*10^4,1\leq m\leq 2*10^5 1n5104,1m2105.

首先,我们明确一点,一棵最小生成树上的最大边一定最小,这一点很容易用kruskal的算法流程来证明.

那么我们就可以考虑一个暴力算法,枚举一棵生成树的最小边,然后依照kruskal算法加入剩下的边来组成生成树,取最优即可.时间复杂度 O ( m 2 log ⁡ n ) O(m^2\log n) O(m2logn).

考虑如何优化这个算法,我们发现枚举的过程不太可能去掉,并查集的时间复杂度去掉没有多大意义,所以我们选择优化每一棵生成树的生成过程.

我们考虑,当我们枚举到一条边时,若我们当前维护的边集中加入这条边不会形成环,我们就将这条边加入.否则我们找到一条形成的环上边权最小的边,然后删掉这条边加入当前的边.当我们维护的边集组成一棵生成树的时候,我们就可以更新答案.

那么我们考虑一棵LCT来维护这道题的这棵带删除和查询链上最小值的并查集即可.

至于维护边权的LCT,拆点维护即可.

代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
const int N=50000,M=200000,INF=1<<29;
struct tree{
  int x,min,g,fa,s[2],rev;
}tr[N+M+9];
int tmp[N+M+9],ttmp;
int n,m,ans=INF,use[M+9];
struct side{
  int x,y,v;
  bool operator < (const side &p)const{return v<p.v;}
}e[M+9];
bool Isroot(int x){return tr[tr[x].fa].s[0]^x&&tr[tr[x].fa].s[1]^x;}
void Pushup(int x){
  int ls=tr[x].s[0],rs=tr[x].s[1];
  if (tr[ls].min<tr[rs].min&&tr[ls].min<tr[x].x) tr[x].min=tr[ls].min,tr[x].g=tr[ls].g;
  else if (tr[rs].min<tr[x].x) tr[x].min=tr[rs].min,tr[x].g=tr[rs].g;
    else tr[x].min=tr[x].x,tr[x].g=x;
}
void Update_rev(int x){tr[x].rev^=1;swap(tr[x].s[0],tr[x].s[1]);}
void Pushdown(int x){
  if (!tr[x].rev) return;
  tr[x].rev=0;
  Update_rev(tr[x].s[0]);Update_rev(tr[x].s[1]);
}
void Rotate(int x){
  int y=tr[x].fa,z=tr[y].fa,k=tr[y].s[1]==x;
  if (!Isroot(y)) tr[z].s[tr[z].s[1]==y]=x;tr[x].fa=z;
  tr[y].s[k]=tr[x].s[k^1];if (tr[x].s[k^1]) tr[tr[x].s[k^1]].fa=y;
  tr[x].s[k^1]=y;tr[y].fa=x;
  Pushup(y);Pushup(x);
}
void Splay(int x){
  int y,z;
  tmp[ttmp=1]=x;
  for (int i=x;!Isroot(i);i=tr[i].fa) tmp[++ttmp]=tr[i].fa;
  for (;ttmp;ttmp--) Pushdown(tmp[ttmp]);
  while (!Isroot(x)){
    y=tr[x].fa,z=tr[y].fa;
    if (!Isroot(y)) tr[y].s[1]==x^tr[z].s[1]==y?Rotate(x):Rotate(y);
    Rotate(x);
  }
  Pushup(x);
}
void Access(int x){
  for (int t=0;x;t=x,x=tr[x].fa)
    Splay(x),tr[x].s[1]=t,Pushup(x);
}
void Makeroot(int x){Access(x);Splay(x);Update_rev(x);}
int Root(int x){Access(x);Splay(x);while (tr[x].s[0]) x=tr[x].s[0];return x;}
void Split(int x,int y){Makeroot(x);Access(y);Splay(y);}
void Link(int x,int y){if (Root(x)==Root(y)) return;Makeroot(x);tr[x].fa=y;}
void Cut(int x,int y){Split(x,y);if (tr[y].s[0]^x||tr[y].s[1]||tr[x].s[1]) return;tr[y].s[0]=tr[x].fa=0;}
int Query_g(int x,int y){Split(x,y);return tr[y].g;}
Abigail into(){
  scanf("%d%d",&n,&m);
  for (int i=0;i<=n;i++) tr[i].x=tr[i].min=INF;
  for (int i=1;i<=m;i++)
    scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v);
}
Abigail work(){
  sort(e+1,e+1+m);
  int h=1,cnt=1;
  for (int i=1;i<=m;i++) tr[i+n].x=e[i].v;
  for (int i=1;i<=m;i++)
    if (Root(e[i].x)^Root(e[i].y)){
      Link(e[i].x,i+n);Link(e[i].y,i+n);
      use[i]=1;
      while (!use[h]) ++h;
      if (++cnt==n) ans=min(ans,e[i].v-e[h].v);
    }else{
      if (e[i].x==e[i].y) continue;
      int g=Query_g(e[i].x,e[i].y)-n;
      Cut(e[g].x,g+n);Cut(e[g].y,g+n);
      use[g]=0;
      Link(e[i].x,i+n);Link(e[i].y,i+n);
      use[i]=1;
      while (!use[h]) ++h;
      if (cnt==n) ans=min(ans,e[i].v-e[h].v);
    }
}
Abigail outo(){
  printf("%d\n",ans);
}
int main(){
  into();
  work();
  outo();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值