最小生成树与最小树形图

最小生成树

从无向图的角度讲,对于一个联通的无向图,我们可以构造出这个无向图的一棵生成树.形式化地说,一棵给定无向图$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;
}
posted @ 2015-06-26 20:52  zball  阅读(879)  评论(0编辑  收藏  举报