1. 程式人生 > >【BZOJ 1977】次小生成樹

【BZOJ 1977】次小生成樹

【題目】

傳送門

題目描述:

C 最近學了很多最小生成樹的演算法,Prim 演算法、Kurskal 演算法、消圈演算法等等。 正當小 C 洋洋得意之時,小 P 又來潑小 C 冷水了。小 P 說,讓小 C 求出一個無向圖的次小生成樹,而且這個次小生成樹還得是嚴格次小的,也就是說: 如果最小生成樹選擇的邊集是 EM,嚴格次小生成樹選擇的邊集是 ES,那麼需要滿足:

eEMvalue(e)<eESvalue(e)\sum_{e\in E_M}value(e)<\sum_{e\in E_S}value(e)

lue(e)

其中 value(e)value(e) 表示邊 ee 的權值。

這下小 C 蒙了,他找到了你,希望你幫他解決這個問題。

輸入格式:

第一行包含兩個整數 nnmm ,表示無向圖的點數與邊數。 接下來 mm 行,每行 33 個數 xx yy zz 表示,點 xx 和點 yy 之間有一條邊,邊的權值為 zz

輸出格式:

包含一行,僅一個數,表示嚴格次小生成樹的邊權和。(資料保證必定存在嚴格次小生成樹)

樣例資料:

輸入 5 6 1 2 1 1 3 2 2 4 3 3 5 4 3 4 3 4 5 6

輸出 11

備註:

【資料範圍】 資料中無向圖無自環; 50

%50\% 的資料:n2000,m3000n≤2000,m≤300080%80\% 的資料:n50000,m100000n≤50000,m≤100000100%100\% 的資料:n100000,m300000n≤100000,m≤300000; 邊權值非負且不超過 10910^9

【分析】

次小生成樹的模板題啦

大致講一下演算法步驟吧

次小生成樹的話,和次短路的思路其實差不多

首先,肯定還是要求出最小生成樹,那麼現在就列舉不在最小生成樹上的邊,加上這條邊之後,肯定就會形成一個環,將這個環上最長的邊(除了新加的那條邊)刪掉之後,就是一個新的生成樹,在新的生成樹中選個最小的就行了

現在就考慮怎麼實現了,就是用倍增

在最小生成樹中,我們用 m

ax1[i][j]max1[i][j] 表示從 ii 往上跳 2j2^j 步的最大值,用 max2[i][j]max2[i][j] 表示從 ii 往上跳 2j2^j 步的次大值(記錄次大值是因為要求的是嚴格次小生成樹,有時不能直接用最大值)

那麼假設現在要新加 (x,y)(x,y) 這條邊,就找 xxyy 路徑上的邊權最(次)大值,直接倍增找就行了(和找 lcalca 差不多)

找到之後,更新,統計最小值就行了,然後就做完了

【程式碼】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define M 1000005
#define ll long long
#define inf 1ll<<60ll
using namespace std;
int n,m,t;
struct edge{int u,v,w;}e[M];
int first[N],v[M],w[M],nxt[M],vis[M];
int dep[N],father[N],fa[N][25],Max1[N][25],Max2[N][25];
bool comp(const edge &p,const edge &q)  {return p.w<q.w;}
int find(int x)  {return x==father[x]?x:father[x]=find(father[x]);}
void add(int x,int y,int z)  {nxt[++t]=first[x];first[x]=t;v[t]=y;w[t]=z;}
ll Kruskal()
{
	int i;ll ans=0;
	sort(e+1,e+m+1,comp);
	for(i=1;i<=m;++i)
	{
		int x=find(e[i].u);
		int y=find(e[i].v);
		if(x!=y)
		{
			vis[i]=1;
			father[x]=y,ans+=e[i].w;
			add(e[i].u,e[i].v,e[i].w);
			add(e[i].v,e[i].u,e[i].w);
		}
	}
	return ans;
}
void dfs(int x)
{
	int i;
	for(i=first[x];i;i=nxt[i])
	{
		if(v[i]==fa[x][0])  continue;
		fa[v[i]][0]=x,dep[v[i]]=dep[x]+1;
		Max1[v[i]][0]=w[i],Max2[v[i]][0]=0;
		dfs(v[i]);
	}
}
void init()
{
	int i,j;
	for(j=1;j<=20;++j)
	{
		for(i=1;i<=n;++i)
		{
			fa[i][j]=fa[fa[i][j-1]][j-1];
			Max1[i][j]=max(Max1[i][j-1],Max1[fa[i][j-1]][j-1]);
			Max2[i][j]=max(Max2[i][j-1],Max2[fa[i][j-1]][j-1]);
			if(Max1[i][j-1]<Max1[fa[i][j-1]][j-1]&&Max2[i][j]<Max1[i][j-1])  Max2[i][j]=Max1[i][j-1];
			if(Max1[i][j-1]>Max1[fa[i][j-1]][j-1]&&Max2[i][j]<Max1[fa[i][j-1]][j-1])  Max2[i][j]=Max1[fa[i][j-1]][j-1];
		}
	}
}
int find(int x,int y,int limit)
{
	int i,ans=0;
	if(dep[x]<dep[y])  swap(x,y);
	for(i=20;~i;--i)
	{
		if(dep[fa[x][i]]>=dep[y])
		{
			ans=max(ans,Max1[x][i]!=limit?Max1[x][i]:Max2[x][i]);
			x=fa[x][i];
		}
	}
	if(x==y)  return ans;
	for(i=20;~i;--i)
	{
		if(fa[x][i]!=fa[y][i])
		{
			ans=max(ans,Max1[x][i]!=limit?Max1[x][i]:Max2[x][i]),x=fa[x][i];
			ans=max(ans,Max1[y][i]!=limit?Max1[y][i]:Max2[y][i]),y=fa[y][i];
		}
	}
	ans=max(ans,Max1[x][0]!=limit?Max1[x][0]:Max2[x][0]);
	ans=max(ans,Max1[y][0]!=limit?Max1[y][0]:Max2[y][0]);
	return ans;
}
ll solve(ll MST)
{
	int i;ll ans=inf;
	for(i=1;i<=m;++i)
	{
		if(!vis[i])
		{
			int temp=find(e[i].u,e[i].v,e[i].w);
			if(temp!=e[i].w)  ans=min(ans,MST-temp+e[i].w);
		}
	}
	return ans;
}
int main()
{
	int i;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i)  father[i]=i;
	for(i=1;i<=m;++i)  scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
	ll MST=Kruskal();
	dep[1]=1,dfs(1);
	init();
	ll ans=solve(MST);
	printf("%lld",ans);
	return 0;
}