Kruskal重构树
定义
首先新建 n 个集合,每个集合恰有一个节点,点权为 0。
每次加边按照边权从小到大加入。
每一次加边会合并两个集合,我们可以新建一个点,点权为加入边的边权,
同时将两个集合的根节点分别设为新建点的左儿子和右儿子。
然后我们将两个集合和新建点合并成一个集合。将新建点设为根。
不难发现,在进行 n-1轮之后我们得到了一棵恰有 n个叶子的二叉树,同时每个非叶子节点恰好有两个儿子。这棵树就叫Kruskal 重构树。
例子
应用
u->v路径上的最大值最小
Kruskal 重构树上两点的 LCA 的权值
= 最小生成树上两个点之间的简单路径上的最大值
= 原图中两个点之间的所有简单路径上最大边权的最小值。
注意:
如果需要求原图中两个点之间的所有简单路径上最小边权的最大值,则在跑 Kruskal 的过程中按边权大到小的顺序加边。
从u出发只经过边权不超过x的边能到达的节点
我们只要在边权升序的Kruskal重构树中找到深度最小的,点权不超过x的节点(一般用树上倍增来实现),那么这个节点的子树就是所求的答案。
如何用倍增来找呢?
我们考虑当前我们找到的这个节点为x,然后我们倍增枚举它的祖先,由于是升序排序,所以它祖先的点的点权必然大于等于它的点权,于是,我们倍增的时候,只要判断如果它的祖先的点权,就直接,就好了。
模板代码
模板题
NOIP2013 货车运输
思路:
转化问题: 求两点之间所有路径中,最短的边的最大值。
Kruskal重构树模板题
代码
#include<bits/stdc++.h>//kruskal重构树,求两点之间所有的路径中,最短的边的最大值 using namespace std; int n,m,q; int nxt[200010],to[200010],head[50010],w[50010],tot;//重构树的边,w是新树点权 int cnt; int f[50010];//kruskal算法的并查集 int fa[50010][20],dep[50010];//用于求lca int vis[50010]; struct orz//用于kruskal算法 { int x,y,val; }a[100010]; char buf[1<<15],*fs,*ft; inline char getc(){return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++;} inline int read() { int x=0,f=1; char ch=getc(); while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getc();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48); ch=getc();} return x*f; } void put(int x) { if(x==0){putchar('0');putchar('\n');return;} if(x<0){putchar('-');x=-x;} int num=0;char ch[16]; while(x) ch[++num]=x%10+'0',x/=10; while(num) putchar(ch[num--]); putchar('\n'); } inline int my(orz a,orz b) { return a.val>b.val; } inline int find(int x) { int r=x,mid,j=x; while(r!=f[r]) r=f[r]; while(j!=f[j]) mid=f[j],f[j]=r,j=mid; return r; } inline void add(int xx,int yy) { to[++tot]=yy;nxt[tot]=head[xx];head[xx]=tot; } void dfs(int x)//预处理lca { vis[x]=1; for(int ct=head[x];ct;ct=nxt[ct]) { int y=to[ct]; fa[y][0]=x; for(int i=1;i<=15;++i) fa[y][i]=fa[fa[y][i-1]][i-1]; dep[y]=dep[x]+1; dfs(y); } } inline int lca(int x,int y)//求解lca { if(dep[x]<dep[y]) swap(x,y); for(int i=15;i>-1;--i) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i]; if(x==y) return x; for(int i=15;i>-1;--i) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } int main() { n=read();m=read(); for(int i=1;i<=m;++i) a[i].x=read(),a[i].y=read(),a[i].val=read(); for(int i=1;i<=n;++i)f[i]=i,f[n+i]=n+i;//并查集初始化 cnt=n;sort(a+1,a+m+1,my);//kruskal算法+重构 for(int i=1;i<=m;++i) { int fx=find(a[i].x),fy=find(a[i].y); if(fx!=fy) f[fx]=f[fy]=++cnt,w[cnt]=a[i].val,add(cnt,fx),add(cnt,fy); } for(int i=cnt;i>n;--i)//预处理lca,注意原图可能不联通,所以我们可能构出了一个森林 if(!vis[i]) dep[i]=1,fa[i][0]=i,dfs(i); q=read();//处理询问 for(int i=1;i<=q;++i) { int xx=read(),yy=read(); if(find(xx)!=find(yy)) put(-1);//两点可能不联通 else put(w[lca(xx,yy)]); } return 0; }
【2021ICPC上海站】H-Life is a Game
题意
你本身有一个权值。
每个点有一个权值,到达一个点可以获得这个权值;
每条边也有一个权值,只有你自己当前权值大于等于边权才可以走这条边。
q次询问,每次给出初始点和初始边权,输出可获得的最大边权。
思路
krustal重构树
一个点可以获得自己子树所有点权之和。
子树求和 + 倍增跳就好了
代码
#include<bits/stdc++.h> using namespace std; #define pii pair<int,int> #define int long long const int N=2e5+10; vector<tuple<int,int,int> >es; vector<int> g[N]; int be,k; int n,m,q; int fa[N]; int f[N][21],sz[N]; int val[N],cnt; int find(int x){ if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x]; } void krus(){ cnt=n;//--------- for(int i=1;i<=n;i++) fa[i]=i; sort(es.begin(),es.end()); int u,v,w; for(int i=0;i<m;i++){ tie(w,u,v)=es[i]; u=find(u),v=find(v); if(u!=v){ val[++cnt]=w; fa[cnt]=fa[u]=fa[v]=cnt; g[u].push_back(cnt); g[cnt].push_back(u); g[v].push_back(cnt); g[cnt].push_back(v); } } } void dfs(int u,int father){ f[u][0]=father; for(int i=1;i<=20;i++){ f[u][i]=f[f[u][i-1]][i-1]; } for(int v: g[u]) { if(v!=father){ dfs(v,u); sz[u]+=sz[v]; } } } void solve(){ cin>>n>>m>>q; for(int i=1;i<=n;i++) cin >> sz[i]; for(int i=0;i<m;i++){ int u,v,w; cin>>u>>v>>w; es.emplace_back(w,u,v); } krus(); dfs(cnt,0); val[0]=1e18+7; while(q--){ cin>>be>>k; int ans=k+sz[be]; while(be!=cnt){ int x=be; for(int i=20;i>=0;i--){ if(val[f[be][i]]<=ans) be=f[be][i]; } if(x==be) break; ans=sz[be]+k; } cout<<ans<<endl; } } signed main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int t=1; while(t--) solve(); return 0; }
P4197 Peaks
思路:
代码:
原文:https://oi-wiki.org/graph/mst/#kruskal-重构树
原文:https://cmwqf.github.io/2019/03/04/浅谈克鲁斯卡尔重构树/
P4197 Peaks
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/16667247.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步