简介
本文将简单介绍如何使用 Link Cut Tree 维护动态图最小生成树。
思路
最小生成树的性质:一个基环树的最小生成树,为将环上边权最大的边删除后所组成的树。
Proof:如果删除环上的其他边,那么删除的边的权一定不大于最大边的边权。所以删最大权的边的树的边权和比其他的都要小。符合最小生成树定义。
如果我们插入边 \((u,v,w)\)。先判断 \(u,v\) 之间是否连通(这个可以简单的用 LCT 完成,不会的去做 P2147 [SDOI2008] 洞穴勘测)。
- 如果不连通,那么就最小生成树上(就是 LCT 上)连边 \((u,v)\)。
- 如果连通,那么先找到路径 \((u,v)\) 上边权最大的边,如果它的边权小于等于 \(w\),那么我们要插入的边是“废边”,直接忽略。否则将边权最大的边 Cut 掉,连上 \((u,v,w)\)。
注意到 LCT 无法直接维护边权,于是我们可以将边拆点之后维护(如果不会去做 SPOJ QTREE - Query on a tree)。
至此,以上操作全部可以用 LCT 完成(只需要维护最大值和最大值位置)。时间复杂度单次期望 \(O(\log n)\)。
代码
这里以 P3366 【模板】最小生成树 为例。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 4e5+5;
namespace LCT{
#define ls (son[i][0])
#define rs (son[i][1])
int son[N][2];
int fa[N];
bool tag[N];
int maxt[N],maxid[N];
int val[N];
inline void pushup(int i){
maxt[i]=val[i],maxid[i]=i;
if(maxt[ls]>maxt[i]){
maxt[i]=maxt[ls];maxid[i]=maxid[ls];
}
if(maxt[rs]>maxt[i]){
maxt[i]=maxt[rs];maxid[i]=maxid[rs];
}
}
inline void reverse(int i){
swap(ls,rs);tag[i]^=1;
}
inline void pushdown(int i){
if(tag[i]){
if(ls) reverse(ls);
if(rs) reverse(rs);
tag[i]=0;
}
}
inline bool get(int i){
return son[fa[i]][1]==i;
}
inline bool is_root(int i){
return son[fa[i]][0]!=i && son[fa[i]][1]!=i;
}
void update(int i){
if(!is_root(i)){
update(fa[i]);
}
pushdown(i);
}
inline void rotate(int p){
int q=fa[p],z=fa[q],k=get(p);
if(!is_root(q)){
son[z][son[z][1]==q]=p;
}
fa[p]=z;
son[q][k]=son[p][!k];
if(son[p][!k]) fa[son[p][!k]]=q;
son[p][!k]=q;
fa[q]=p;
pushup(q);
pushup(p);
}
inline void splay(int i){
update(i);
for(int f;f=fa[i],!is_root(i);rotate(i)){
if(!is_root(f)){
rotate(get(f)==get(i)?f:i);
}
}
}
inline void access(int i){
int p;
for(p=0;i;p=i,i=fa[i]){
splay(i);
son[i][1]=p;
pushup(i);
}
}
inline int find(int i){
access(i);
splay(i);
while(ls) pushdown(i),i=ls;
splay(i);
return i;
}
inline void make_root(int i){
access(i);
splay(i);
reverse(i);
}
inline void split(int u,int v){
make_root(u);
access(v);splay(v);
}
inline void link(int u,int v){
make_root(u);
if(find(v)!=u){
fa[u]=v;
}
}
inline void cut(int i){
splay(i);
fa[ls]=fa[rs]=0;
}
}
int ret=0,ec=0;
signed main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
LCT::val[i+n]=w;
if(LCT::find(u)!=LCT::find(v)){
LCT::link(u,i+n);LCT::link(i+n,v);
ret += w;
ec++;
continue;
}
LCT::split(u,v);
int mxid=LCT::maxid[v],mxv=LCT::maxt[v];
if(mxv<=w) continue;
ret -= mxv;
LCT::cut(mxid);
LCT::link(u,i+n);
LCT::link(i+n,v);
ret += w;
}
if(ec==(n-1)) cout<<ret;
else cout<<"orz";
return 0;
}