【题目描述】
给定一张 N 个点 M 条边的无向图,求无向图的严格次小生成树。
设最小生成树的边权之和为sum,严格次小生成树就是指边权之和大于 sum 的生成树中最小的一个。
【输入】
第一行包含两个整数 N 和 M,表示无向图的点数与边数;
接下来 M 行,每行三个数 x,y,z,表示点 x 和点y 之间有一条边,边的权值为 z
【输出】
包含一行,仅一个数,表示严格次小生成树的边权和。
【样例输入】
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
【样例输出】
11
思路
首先我们可以证明次小生成树一定在一颗最小生成树的邻集中(即只有一条边不同)。
所以我们可以先求出最小生成树,枚举每一条非树边,利用lca维护非树边两端点之间路径上的最大值和次大值,并尝试用当前非树边替换,最后打擂台比较,得到最小生成树邻集中边权和最小的一颗生成树,就是次小生成树。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#define re register
using namespace std;
long long n,m,a,b,c;
struct node{
long long u,v,w;
}e[1000001],E[1000001];
typedef pair<int,int>T;
bool t[1000001];
long long fa[100001];
long long f[100001];
long long nxp[1000001],cnt=0;
long long dep[100001];
long long ff[100001][20];
long long fir[100001][20];
long long sec[100001][20];
inline void add(long long u,long long v,long long w){e[++cnt].u=u;e[cnt].w=w;e[cnt].v=v;nxp[cnt]=f[u];f[u]=cnt;}
long long cmp(node a,node b){return a.w<b.w;}
long long find(long long x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void dfs(long long u)
{
for(long long re i=1;(1<<i)<=dep[u];i++)
{
ff[u][i]=ff[ff[u][i-1]][i-1];
fir[u][i]=max(fir[u][i-1],fir[ff[u][i-1]][i-1]);
if(fir[u][i-1]!=fir[ff[u][i-1]][i-1])
sec[u][i]=min(fir[u][i-1],fir[ff[u][i-1]][i-1]);
sec[u][i]=max(sec[u][i],sec[u][i-1]);
sec[u][i]=max(sec[u][i],sec[ff[u][i-1]][i-1]);
}
for(long long re i=f[u];i;i=nxp[i])
{
long long v=e[i].v;
if(!dep[v])
{
dep[v]=dep[u]+1;
fir[v][0]=e[i].w;
ff[v][0]=u;
dfs(v);
}
}
}
inline T lca(long long a,long long b)
{
long long fi=-0x7ffff,se=-0x7ffff;
if(dep[a]<dep[b])swap(a,b);
long long tt=dep[a]-dep[b];
for(long long re i=0;(1<<i)<=tt;i++)
if(tt&(1<<i))
{
if(fi!=fir[a][i])
se=max(se,min(fi,fir[a][i]));
se=max(se,sec[a][i]);
fi=max(fi,fir[a][i]);
a=ff[a][i];
}
if(a==b)return make_pair(fi,se);
for(long long re i=18;i>=0;i--)
{
if(ff[a][i]!=ff[b][i])
{
if(fi!=fir[a][i])
se=max(se,min(fi,fir[a][i]));
se=max(se,sec[a][i]);
fi=max(fi,fir[a][i]);
a=ff[a][i];
if(fi!=fir[b][i])
se=max(se,min(fi,fir[b][i]));
se=max(se,sec[b][i]);
fi=max(fi,fir[b][i]);
b=ff[b][i];
}
}
se=max(se,sec[b][0]);
se=max(se,sec[a][0]);
if(fi!=fir[a][0])
se=max(se,min(fi,fir[a][0]));
if(fi!=fir[b][0])
se=max(se,min(fi,fir[b][0]));
fi=max(fi,fir[a][0]);
fi=max(fi,fir[b][0]);
return make_pair(fi,se);
}
long long ans=0;
long long mn=0x7f7f7f7f;
int main()
{
scanf("%lld%lld",&n,&m);
memset(sec,-127/3,sizeof(sec));
for(long long re i=1;i<=n;i++)fa[i]=i;
for(long long re i=1;i<=m;i++)
scanf("%lld%lld%lld",&E[i].u,&E[i].v,&E[i].w);
sort(E+1,E+m+1,cmp);
for(long long re i=1;i<=m;i++)
{
long long u=E[i].u,v=E[i].v;
long long w=E[i].w;
if(find(u)!=find(v))
{
fa[find(u)]=find(v);
add(u,v,w);
add(v,u,w);
ans+=w;
t[i]=1;
}
}
dep[1]=1;
dfs(1);
for(long long re i=1;i<=m;i++)
{
if(!t[i])
{
long long u=E[i].u;
long long v=E[i].v;
long long w=E[i].w;
T t=lca(u,v);
if(t.first!=w)
mn=min(mn,w-t.first);
else mn=min(mn,w-t.second);
}
}
printf("%lld",ans+mn);
}