【BZOJ1977】[BeiJing2010組隊]次小生成樹 Tree Kruskal+倍增LCA

次小生成樹:

先用Kruskal求出最小生成樹(當然用Prim……也沒關係~),再枚舉未出現在最小生成樹中的邊加入到其中,則形成了一個環。求出環中除新加邊外權值最大的邊(使得新增權值最小)並刪去,即得到次小生成樹。

事實上,環路中權值最小的邊就是新加入的邊兩端點間的路徑上權值最小的邊,此處可以用倍增LCA維護。另外由於需求出嚴格次小生成樹,於是還要維護路徑上邊權的嚴格次小值。詳見代碼。

[BeiJing2010組隊]次小生成樹 C++代碼實現:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x7f7f7f7f
#define N 100010
#define M 300010
int n,m;
bool vis[M];
int cnt,f[N],head[N],to[N*2],next[N*2],val[N*2];
int deep[N],fa[N][17],d1[N][17],d2[N][17],ans=INF;
long long tot;
struct data{int x,y,z;}a[M];
int find(int x)
{
	return x==f[x]?x:f[x]=find(f[x]);
}
bool cmp(data x,data y)
{
	return x.z<y.z;
}
void add(int x,int y,int z)
{
	to[++cnt]=y;
	val[cnt]=z;
	next[cnt]=head[x];
	head[x]=cnt;
}
void dfs(int x,int d)
{
	for(int i=1;i<=16&&deep[x]<=(1<<i);i++)
		fa[x][i]=fa[fa[x][i-1]][i-1],
		d1[x][i]=max(d1[x][i-1],d1[fa[x][i-1]][i-1]),
		d2[x][i]=d1[x][i-1]==d1[fa[x][i-1]][i-1]?
			max(d2[x][i-1],d2[fa[x][i-1]][i-1]):
			max(max(d2[x][i-1],d2[fa[x][i-1]][i-1]),min(d1[x][i-1],d1[fa[x][i-1]][i-1]));
	deep[x]=d;
	for(int i=head[x];i;i=next[i])
		if(to[i]!=fa[x][0])
			fa[to[i]][0]=x,
			d1[to[i]][0]=val[i],
			dfs(to[i],d+1);
}

int lca(int x,int y)
{
	if(deep[x]<deep[y]) swap(x,y);
	for(int i=16;i>=0;i--)
		if(deep[fa[x][i]]>=deep[y])
			x=fa[x][i];
	if(x==y) return x;
	int re;
	for(int i=16;i>=0;i--)
		if(fa[x][i]!=fa[y][i])
			x=fa[x][i],y=fa[y][i];
		else re=fa[x][i];
	return re;
}
int getans(int x,int y,int z)
{
	int t=lca(x,y),re1=0,re2=0;
	int dis1=deep[x]-deep[t],dis2=deep[y]-deep[t];
	for(int i=16;i>=0;i--)
	{
		if(dis1&(1<<i))
		{
			re2=re1==d1[x][i]?max(re2,d2[x][i]):max(min(re1,d1[x][i]),max(re2,d2[x][i]));
			re1=max(re1,d1[x][i]);
			x=fa[x][i];
		}
		if(dis2&(1<<i))
		{
			re2=re1==d1[y][i]?max(re2,d2[y][i]):max(min(re1,d1[y][i]),max(re2,d2[y][i]));
			re1=max(re1,d1[y][i]);
			y=fa[y][i];
		}
	}
	return re1==z?z-re2:z-re1;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=n;i++)
		f[i]=i;
	for(int t=0,i=1;i<=m;i++)
	{
		int fx=find(a[i].x),fy=find(a[i].y);
		if(fx!=fy)
		{
			f[fx]=fy;
			vis[i]=true;
			add(a[i].x,a[i].y,a[i].z);
			add(a[i].y,a[i].x,a[i].z);
			tot+=a[i].z;
			if(++t==n-1) break;
		}
	}
	dfs(1,1);
	for(int i=1;i<=m;i++)
		if(!vis[i])
			ans=min(ans,getans(a[i].x,a[i].y,a[i].z));
	cout<<tot+ans;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章