最小生成树
从无向图的角度讲,对于一个联通的无向图,我们可以构造出这个无向图的一棵生成树.形式化地说,一棵给定无向图$G=(V,E)$的生成树$S$是一个图$(V,E')$,满足
1) $S$联通.
2) $S$中不存在环.
我们可以定义边的权值$w(E)$,一般来说,$w(E)$的值相对边是独立的(互不影响)的.一棵生成树的权值$w(T)$就是它的边的权值和.最小生成树,就是使$w(T)$最小的生成树$T\in g(G)$.$g(G)$是$G$生成树的集合.)
最小生成树一般用著名的Kruskal算法求.这个算法很优美,写起来也很方便.
但是这里要介绍一个不常见但是非常好用的新算法,对最小树形图的算法也很有启发.
Borůvka 算法
这个算法的基本思想是每次寻找可以放入最小生成树的边.算法基本步骤:
如果没有生成最小生成树: -> 1 遍历所有边,找出点的最小外联边 -> 2 将这些外联边加入最小生成树,形成一些联通分量 -> 3 对于每个新形成的联通分量缩点
时间复杂度分析
每次对于一个点都会至少找出另外一个点与它组成联通块,最坏情况需要迭代$\log_2{|V|}$次,每次$O(E)$,因此时间复杂度上限$O(E\log_2{V})$,对于随机图会更快些.这里假设并查集查找与合并的时间复杂度$O(1)$.
最小树形图
讲最小生成树的目的当然是为了讲最小树形图.
对于一个有向图$G=(V,E)$,我们可以定义出它的树形图集合$g(G)$,其中的每一个元素$S\in g(G)=(V,E')$满足:
1) $S$半联通(单向联通).
2) $S$中没有环.
可以看出这是一棵有根树.
如图中的橙色边标出了这个图的一个树形图.
补个约大爷版的代码(不是约大爷写的...对着约大爷的代码打的...)
#include <cstdio>
#include <algorithm>
#include <set>
#define fon(i,s) for(int i=0;i<s; ++i)
#define fone(i,s) for(int i=0;i<=s;++i)
#define fox(i,f,t) for(int i=f;i<t; ++i)
#define foxe(i,f,t) for(int i=f;i<=t;++i)
#define fdn(i,s) for(int i=s;i; --i)
#define fdne(i,s) for(int i=s;~i; --i)
#define fdx(i,f,t) for(int i=f;i>t; --i)
#define fdxe(i,f,t) for(int i=f;i>=t;--i)
struct Info{
int S,C,U;
Info* c[2];
inline Info(){}
inline Info(int CC){ c[0]=0,c[1]=0,S=0,U=0,C=CC; }
inline Info(bool ibp,Info* c0,Info* c1){
S=(ibp<<1)-1,c[0]=c0,c[1]=c1,U=0;
C=c[0]->C+S*c[1]->C;
}
inline Info* operator()(int CC){ c[0]=0,c[1]=0,S=0,U=0,C=CC;return this; }
inline Info* operator()(bool ibp,Info* c0,Info* c1){
S=(ibp<<1)-1,c[0]=c0,c[1]=c1,U=0;
C=c[0]->C+S*c[1]->C;
return this;
}
inline void pick(){++U;}
inline void inhr(){if(S){c[0]->U+=U,c[1]->U+=S*U,U=0;}}
} NIL(0);
#define VM 500001
struct _uvec{
Info* a[VM];
int l,p;
inline void push_back(Info* b){a[l++]=b;}
inline Info* operator[](int n){return a[n];}
inline Info** operator+(int n){return a+n;}
inline int riter(){return p--;}
inline void startRIterSession(){p=l;}
inline Info* iterP(){return a[p];}
} dr;
Info IS[VM];
int ISL;
#define new_Info IS[ISL++]
inline Info* Add(Info* a,Info* b){
if(a==&NIL) return b;
if(b==&NIL) return a;
Info* n=new_Info(1,a,b);
dr.push_back(n);
return n;
}
inline Info* Sub(Info* a,Info* b){
if(b==&NIL) return a;
Info* n=new_Info(0,a,b);
dr.push_back(n);
return n;
}
struct Leftist{
Leftist* s[2];
Info *vf,*Msk;
int vs,dist;
inline Leftist(Info *v=&NIL,int sx=0){ s[0]=NULL,s[1]=NULL,dist=1,vf=v,vs=sx,Msk=&NIL; }
inline Leftist* operator()(Info *v=&NIL,int sx=0){ s[0]=NULL,s[1]=NULL,dist=1,vf=v,vs=sx,Msk=&NIL;return this;}
inline int key(){
return vf->C;
}
};
inline int dist(Leftist* x){return (x)?x->dist:0;}
inline void Push(Leftist* x){//Info pushdown
if(x->s[0]) x->s[0]->Msk=Add(x->s[0]->Msk,x->Msk);
if(x->s[1]) x->s[1]->Msk=Add(x->s[1]->Msk,x->Msk);
x->vf=Sub(x->vf,x->Msk),x->Msk=&NIL;
}
Leftist* Merge(Leftist* l,Leftist* r){
if(l==NULL) return r;
if(r==NULL) return l;
Push(l),Push(r);
if(l->key()>r->key()) std::swap(l,r);
l->s[1]=Merge(l->s[1],r);
if(dist(l->s[0])<dist(l->s[1])) std::swap(l->s[0],l->s[1]);
l->dist=dist(l->s[1])+1;
return l;
}
Leftist* Extract(Leftist* &x){
Push(x);Leftist* ret=x;
x=Merge(x->s[0],x->s[1]);
return ret;
}
#define MAXN 100012
#define MAXM MAXN
int N,M,hl;
Leftist *heap[MAXN],_h[VM];
#define _L _h[hl++]
struct djset{
int f[MAXN];
inline void Init(){
int u=1+(N<<2);
fone(i,u) f[i<<2]=-1,f[i<<2|1]=-1,f[i<<2|2]=-1,f[i<<2|3]=-1;
}
int find(int x){return ~f[x]?(f[x]=find(f[x])):x;}
inline bool uni(int x,int y){
x=find(x),y=find(y);
if(x==y) return false;
return (f[y]=x);
}
inline bool cnx(int x,int y){
return find(x)==find(y);
}
} weak,strong;
std::set<int> activeStrong;//I don't want to write a set implementation T^T
inline void Join(int u,int v){
strong.uni(u,v);
heap[u]=Merge(heap[u],heap[v]),heap[v]=NULL,activeStrong.insert(u);
}
Info baseInfo[MAXM],*prevCost[MAXN];
int prev[MAXN],Ans;
inline bool Connected(){
foxe(i,1,N) if(!weak.cnx(1,i)) return 0;
return 1;
}
struct _veci{
int s[VM],sl;
inline void push_back(int n){s[sl++]=n;}
inline void clear(){sl=0;}
inline int back(){return s[sl-1];}
inline int operator[](int n){return s[n];}
inline int size(){return sl;}
} LIZ;
int main(){
scanf("%d%d",&N,&M);
foxe(i,1,N) heap[i]=NULL;
foxe(i,1,M){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
baseInfo[i](w);
if(v==1) continue;
heap[v]=Merge(heap[v],_L(baseInfo+i,u));
}
activeStrong.clear();
foxe(i,2,N) activeStrong.insert(i);
weak.Init(),strong.Init(),Ans=0;
while(activeStrong.size()){
int s0=*activeStrong.begin();
if(heap[s0]==NULL){ activeStrong.erase(s0);continue; }
Leftist* pdi=Extract(heap[s0]);
int S=strong.find(pdi->vs);
if(S==s0) continue;
if(!weak.cnx(pdi->vs,s0)){
prev[s0]=pdi->vs,prevCost[s0]=pdi->vf,Ans+=pdi->vf->C,pdi->vf->pick();
weak.uni(pdi->vs,s0);
activeStrong.erase(s0);
continue;
}
LIZ.clear();
LIZ.push_back(strong.find(pdi->vs));
while(LIZ.back()!=s0) LIZ.push_back(strong.find(LIZ.back()));
Ans+=pdi->vf->C;
pdi->vf->pick();
if(heap[s0]) heap[s0]->Msk=Add(heap[s0]->Msk,pdi->vf);
int SZ=LIZ.size();
fon(i,SZ-1) if(heap[LIZ[i]]) heap[LIZ[i]]->Msk=Add(heap[LIZ[i]]->Msk,prevCost[LIZ[i]]);
fon(i,SZ-1) Join(s0,LIZ[i]);
}
if(!Connected()){ printf("-1\n"); return 0; }
printf("%d\n",Ans);
LIZ.clear(),dr.startRIterSession();
while(dr.riter()) dr.iterP()->inhr();
foxe(i,1,M) if((baseInfo[i].U)&&(baseInfo[i].C)) LIZ.push_back(i);
fon(i,Ans) printf("%d%s",LIZ[i],(i==Ans-1)?"\n":" ");
return 0;
}