次小生成树:
先用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;
}