洛谷 p4234 最小差值生成树

本文介绍了一种使用链剖树(LCT)求解边权最长边与最短边差值最小的生成树问题的方法。通过将边按权值排序,并在构建生成树过程中实时更新答案,遇到环则替换环上的最小边。利用LCT维护树的性质。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

求最长边与最短边差值最小的生成树.

题解

LCT裸题.
将边按照边权从小到大排序,产生生成树的同时立即更新答案.
如果加入一条边的时候出现了环,把环上最小的边去掉加入该边.
每次跑最小值即可,用LCT可以较方便地维护.
为什么RE了啊啊啊啊啊啊啊啊啊!!!!!!!!!!!!!!!

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int yuzu=4e5,inf=0x3f3f3f3f;
typedef int fuko[yuzu|10];
fuko vis;
struct edge{
int u,v,w;
void rd(){u=read(),v=read(),w=read();}
void wt(){printf("%d %d %d\n",u,v,w);}
bool operator <(const edge &b) const{
  return w<b.w;
  }
}e[yuzu|10];

struct link_cut_tree{
fuko fa,ch[2],val,tag,xiao;
#define ls(x) ch[0][x]
#define rs(x) ch[1][x]
#define ws(x,y) (rs(x)==y)
int nrt(int x){return ls(fa[x])==x||rs(fa[x])==x;}
void rev(int x){swap(ls(x),rs(x)),tag[x]^=1;}
void push_down(int x){if (tag[x]) rev(ls(x)),rev(rs(x)),tag[x]=0;} 
void pushall(int x){if (nrt(x)) pushall(fa[x]); push_down(x);} 
void push_up(int x){
  xiao[x]=val[x];
  if (e[xiao[ls(x)]].w<e[xiao[x]].w) xiao[x]=xiao[ls(x)];
  if (e[xiao[rs(x)]].w<e[xiao[x]].w) xiao[x]=xiao[rs(x)];
  }
void zhuan(int x){
  int y=fa[x],z=fa[y],k=ws(y,x),ps=ch[!k][x];
  if (nrt(y)) ch[ws(z,y)][z]=x;
  ch[k][y]=ps,ch[!k][x]=y;
  if (ps) fa[ps]=y;
  fa[x]=z,fa[y]=x,push_up(y);
  }
void splay(int x){
  pushall(x);
  for (;nrt(x);zhuan(x)){
    int y=fa[x],z=fa[y];
    if (nrt(y)) zhuan(ws(z,y)^ws(y,x)?x:y);
    }push_up(x);
  }
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs(x)=y,push_up(x);}
void mkrt(int x){access(x),splay(x),rev(x);}
void split(int x,int y){mkrt(x),access(y),splay(y);}
int fdrt(int x){
  access(x),splay(x);
  for (;ls(x);x=ls(x)) push_down(x);
  return x;
  }
int link(int x,int y){mkrt(x),fa[x]=y;}
int cut(int x,int y){split(x,y),fa[x]=ls(y)=0,push_up(y);}
}my_;
#define val my_.val
#define xiao my_.xiao
#define access my_.access
#define split my_.split
#define mkrt my_.mkrt
#define splay my_.splay
#define link my_.link
#define cut my_.cut
#define fdrt my_.fdrt
int main(){
int i,n=read(),m=read(),llx=inf;
for (i=1;i<=m;++i) e[i].rd();
e[0].w=inf,sort(e+1,e+m+1);
int cnt=0,k=1;
for (i=0;i<=n+m;++i) val[i]=i<=n?0:i-n;
for (i=1;i<=m;++i){
  int u=e[i].u,v=e[i].v,w=e[i].w;
  if (u^v){
    if (fdrt(u)==fdrt(v)){
      split(u,v);
      int id=xiao[v];
      vis[id]=0;
      cut(e[id].u,id+n),cut(id+n,e[id].v),--cnt;
      } 
    link(u,i+n),link(i+n,v),++cnt;
    vis[i]=1;
    }
  if (cnt>=n-1){
    for (;!vis[k];++k);
    llx=min(llx,w-e[k].w);
    } 
  }
write(llx);
}

谢谢大家.

### 边权差值最小生成树概念 边权差值最小生成树是指在一给定无向加权图中找到一棵生成树,使得这棵树中的最大边权重与最小边权重之差尽可能小。这一目标不同于传统的最小生成树问题,后者旨在使所有边权重总和最小化。 ### 算法描述 为了构建边权差值最小生成树,可以考虑使用二分查找结合广度优先搜索(BFS)或深度优先搜索(DFS)[^1]的方法来解决这个问题: - 首先对所有的边按照其权重进行升序排序。 - 使用二分查找确定可能的最大边权重范围内的最佳解。 - 对于每一个中间值mid,在当前范围内选取不超过此值的所有边构成子图,并尝试在这个子图内寻找一颗生成树。 - 如果能够成功找到,则说明存在更优解的可能性;反之则提高下限继续探索更大的区间直到收敛至最优解为止。 这种方法通过不断调整候选集的方式逐步逼近全局最优解,从而有效地解决了边权差异最小化的挑战。 ### Python实现示例 下面是一个简单的Python程序框架用于计算边权差值最小生成树: ```python from collections import defaultdict, deque import sys def bfs(graph, start, visited): queue = deque([start]) count = 0 while queue: node = queue.popleft() if not visited[node]: visited[node] = True count += 1 for neighbor in graph[node]: if not visited[neighbor]: queue.append(neighbor) return count == len(graph) def min_max_spanning_tree(edges, n_nodes): edges.sort(key=lambda edge: (edge[2], edge[0], edge[1])) low, high = edges[0][2], edges[-1][2] best_diff = float('inf') result_edges = [] while low <= high: mid = (low + high) >> 1 temp_graph = defaultdict(list) selected_edges = [(u,v,w) for u,v,w in edges if w<=mid] for u, v, _ in selected_edges: temp_graph[u].append(v) temp_graph[v].append(u) visited = [False]*n_nodes is_connected = bfs(temp_graph, 0, visited) max_weight_in_selection = max((w for _,_,w in selected_edges), default=-sys.maxsize) min_weight_in_selection = min((w for _,_,w in selected_edges), default=sys.maxsize) current_difference = max_weight_in_selection - min_weight_in_selection if is_connected and current_difference < best_diff: best_diff = current_difference result_edges = selected_edges[:] high = mid - 1 elif not is_connected or current_difference >= best_diff: low = mid + 1 return result_edges # Example usage edges = [ (0, 1, 6), (1, 2, 7), (2, 0, 5), (0, 3, 8), (3, 4, 9), (4, 5, 10), (5, 3, 11), (1, 6, 12), (6, 7, 13), (7, 5, 14) ] print(min_max_spanning_tree(edges, 8)) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值