【BZOJ 1977】次小生成樹
【題目】
題目描述:
小 C 最近學了很多最小生成樹的演算法,Prim 演算法、Kurskal 演算法、消圈演算法等等。 正當小 C 洋洋得意之時,小 P 又來潑小 C 冷水了。小 P 說,讓小 C 求出一個無向圖的次小生成樹,而且這個次小生成樹還得是嚴格次小的,也就是說: 如果最小生成樹選擇的邊集是 EM,嚴格次小生成樹選擇的邊集是 ES,那麼需要滿足:
其中 表示邊 的權值。
這下小 C 蒙了,他找到了你,希望你幫他解決這個問題。
輸入格式:
第一行包含兩個整數 和 ,表示無向圖的點數與邊數。 接下來 行,每行 個數 表示,點 和點 之間有一條邊,邊的權值為 。
輸出格式:
包含一行,僅一個數,表示嚴格次小生成樹的邊權和。(資料保證必定存在嚴格次小生成樹)
樣例資料:
輸入 5 6 1 2 1 1 3 2 2 4 3 3 5 4 3 4 3 4 5 6
輸出 11
備註:
【資料範圍】 資料中無向圖無自環; 的資料:; 的資料:; 的資料:; 邊權值非負且不超過 。
【分析】
次小生成樹的模板題啦
大致講一下演算法步驟吧
次小生成樹的話,和次短路的思路其實差不多
首先,肯定還是要求出最小生成樹,那麼現在就列舉不在最小生成樹上的邊,加上這條邊之後,肯定就會形成一個環,將這個環上最長的邊(除了新加的那條邊)刪掉之後,就是一個新的生成樹,在新的生成樹中選個最小的就行了
現在就考慮怎麼實現了,就是用倍增啦
在最小生成樹中,我們用 表示從 往上跳 步的最大值,用 表示從 往上跳 步的次大值(記錄次大值是因為要求的是嚴格次小生成樹,有時不能直接用最大值)
那麼假設現在要新加 這條邊,就找 到 路徑上的邊權最(次)大值,直接倍增找就行了(和找 差不多)
找到之後,更新,統計最小值就行了,然後就做完了
【程式碼】
#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;
}