定义:
给定一个带边权的图,把图的所有生成树按照权值哦才能高效到大排序,第二小的成为次小生成树。
定理:
对于一张无向图来说,如果存在最小生成树和严格的次小生成树,那么对于任何一颗最小生成树,都存在一棵严格的次小生成树使得两棵树只有一边不同。
方法
这道题我们有两种思路。
- 1 、我们可以先求出最小生成树,然后在最小生成树里面枚举最小生成树的每一条边删去然后再假设其他的边。
- 2、 我们先求出最小生成树,然后我们去枚举非树边,然后将这条边加入树中,此时必然形成了一个环,同时从树中删掉一条边(这条边是环上除了我们加的边最大的一条边)使得最终的图还是一棵树。
在这里我们主要讨论方法二怎么实现。
首先我们要利用 K r u s k a l Kruskal Kruskal 求出最小生成树,假设最小生成树的答案为 r e s res res 。
然后我们还要求出加边后这个环上除了我们加的边最大的边,借助上图也就是求 5 → 6 5\rightarrow 6 5→6 路径上的最大边。这个就要借助 L C A LCA LCA 了,具体处理见代码。
遍历所有非树边,维护最小值 a n s = m i n ( a n s , r e s + W − M a x ( V , E ) ) ans=min(ans,res+W-Max(V,E)) ans=min(ans,res+W−Max(V,E)) ,最终 a n s ans ans 即是次小生成树。
/*
by:lwq132lwq
*/
#include<bits/stdc++.h>
#define IO ios::sync_with_stdio(false);cin.tie(0);
#define ll long long
#define re register
#define il inline
#define lb(x) ((x)&(-x))
#define pb push_back
#define fr first
#define sc second
#define PI pair<int,int>
#define rep(i,a,n) for(register int i=a;i<=n;i++)
#define fep(i,a,n) for(register int i=n;i>=a;i--)
using namespace std;
template <class T> void read(T &x){
x=0;int f=0;char ch=getchar();
while(ch<'0'||ch>'9'){f|=(ch=='-');ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;return ;
}
bool cmp(int x,int y){return x>y;}
int gcd(int a, int b){return b ? gcd(b,a%b):a;}
int max(int a,int b){if(a>b) return a;else return b;}
int min(int a,int b){if(a<b) return a;else return b;}
int qkpow(int x,int y){int ans=1;while(y){if(y&1) ans*=x;y>>=1;x*=x;}return ans;}
const double eps=1e-6;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
struct edge{
int x,y,w;
bool used;
bool operator < (const edge &a) const
{
return w<a.w;
}
}E[300010];
int F[100010],f[100010][20],d1[100010][20],d2[100010][20];
int head[300010],d[100010],t,tot,n,m,cnt;
struct note
{
int to,nex,dis;
}e[300010];
void add(int x,int y,int z)
{
e[++tot]={y,head[x],z};head[x]=tot;
}
int find(int x)
{
if(x==F[x]) return x;
return F[x]=find(F[x]);
}
ll kruskal()
{
for(int i=1;i<=n;i++) F[i]=i;
sort(E+1,E+1+m);
ll res=0;
for(int i=1;i<=m;i++)
{
int x=find(E[i].x),y=find(E[i].y),w=E[i].w;
if(x==y) continue;
F[x]=y;
res+=w;
E[i].used=1;
}
return res;
}
void build()
{
for(int i=1;i<=m;i++)
{
if(E[i].used)
{
int x=E[i].x,y=E[i].y,w=E[i].w;
add(x,y,w);add(y,x,w);
}
}
}
void bfs()
{
queue<int>q;q.push(1);d[1]=1;
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].nex)
{
int y=e[i].to,z=e[i].dis;
if(d[y]) continue;
d[y]=d[x]+1;
f[y][0]=x;
d1[y][0]=z,d2[y][0]=-inf;
for(int k=1;k<=t;k++)
{
int anc=f[y][k-1];
f[y][k]=f[anc][k-1];
d1[y][k]=d2[y][k]=-inf;
int dis[4]={d1[y][k - 1], d2[y][k - 1], d1[anc][k - 1], d2[anc][k - 1]};
for(int j=0;j<4;j++)
{
int dd=dis[j];
if (dd>d1[y][k]) d2[y][k]=d1[y][k],d1[y][k]=dd;
else if(dd!=d1[y][k]&&dd>d2[y][k]) d2[y][k]=dd;
}
}
q.push(y);
}
}
}
int dis[maxn*2];
int lca(int x,int y,int w)
{
cnt=0;
if(d[x]<d[y]) swap(x,y);
for(int k=t;k>=0;k--)
if(d[f[x][k]]>=d[y])
{
dis[++cnt]=d1[x][k];
dis[++cnt]=d2[x][k];
x=f[x][k];
}
if(x!=y)
{
for(int k=t;k>=0;k--)
{
if(f[x][k]!=f[y][k])
{
dis[++cnt]=d1[x][k];
dis[++cnt]=d2[x][k];
dis[++cnt]=d1[y][k];
dis[++cnt]=d2[y][k];
x=f[x][k],y=f[y][k];
}
}
dis[++cnt]=d1[x][0];
dis[++cnt]=d1[y][0];
dis[++cnt]=d2[x][0];
dis[++cnt]=d2[y][0];
}
int dis1=-inf,dis2=-inf;
for(int i=1;i<=cnt;i++)
{
int dd=dis[i];
if(dd>dis1) dis2=dis1,dis1=dd;
else if(dd!=dis1&&dd>dis2) dis2=dd;
}
if(w>dis1) return w-dis1;
if(w>dis2) return w-dis2;
return inf;
}
int main()
{
read(n);read(m); t=(int)(log(n)/log(2))+1;
for(int i=1;i<=m;i++)
{
int x,y,z;
read(x);read(y);read(z);
E[i]={x,y,z,0};
}
ll sum=kruskal();
build();
bfs();
ll res=1e18;
for(int i=1;i<=m;i++)
{
if(!E[i].used)
{
int x=E[i].x,y=E[i].y,z=E[i].w;
res=min(res,sum+lca(x,y,z));
}
}
cout<<res;
return 0;
}