题目
给定一张 N 个点 M 条边的无向图,求无向图的严格次小生成树。
设最小生成树的边权之和为sum,严格次小生成树就是指边权之和大于sum的生成树中最小的一个。
输入格式
第一行包含两个整数N和M。
接下来M行,每行包含三个整数x,y,z,表示点x和点y之前存在一条边,边的权值为z。
输出格式
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
数据范围
N≤105,M≤3∗105
思路
lca次小生成树。倍增找树上路径最大边即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;
const int N=100010,M=300010,inf=0x3f3f3f3f;
int n,m;
struct Edge
{
int a,b,w;
bool used;
bool operator< (const Edge &t) const
{
return w<t.w;
}
}edge[M];
int p[N];
int h[N],e[M],w[M],ne[M],idx;
int depth[N],fa[N][17],d1[N][17],d2[N][17];
int q[N];
void add(int a,int b,int c)
{
e[idx] = b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int find(int x)
{
if(p[x]!=x) p[x]=find(p[x]);
return p[x];
}
ll kruskal()
{
for(int i=1;i<=n;i++) p[i]=i;
sort(edge,edge+m);
ll res = 0;
for(int i=0;i<m;i++)
{
int a=find(edge[i].a),b=find(edge[i].b),w=edge[i].w;
if(a!=b)
{
p[a]=b;
res+=w;
edge[i].used=true;
}
}
return res;
}
void build()
{
memset(h,-1,sizeof h);
for(int i=0;i<m;i++)
if(edge[i].used)
{
int a=edge[i].a,b=edge[i].b,w=edge[i].w;
add(a,b,w),add(b,a,w);
}
}
void bfs()
{
memset(depth,0x3f,sizeof depth);
depth[0]=0,depth[1]=1;
q[0]=1;
int hh=0,tt=0;
while(hh<=tt)
{
int t=q[hh++];
for(int i=h[t];~i;i=ne[i])
{
int j=e[i];
if(depth[j]>depth[t]+1)
{
depth[j]=depth[t]+1;
q[++tt]=j;
fa[j][0]=t;
d1[j][0]=w[i],d2[j][0]=-inf;
for(int k=1;k<=16;k++)
{
int anc=fa[j][k-1];
fa[j][k]=fa[anc][k-1];
int distance[4]={d1[j][k-1],d2[j][k-1],d1[anc][k-1],d2[anc][k-1]};
d1[j][k]=d2[j][k]=-inf;
for(int u=0;u<4;u++)
{
int d=distance[u];
if(d>d1[j][k]) d2[j][k]=d1[j][k],d1[j][k]=d;
else if(d!=d1[j][k] && d>d2[j][k]) d2[j][k]=d;
}
}
}
}
}
}
int lca(int a,int b,int w)
{
static int distance[N*2];
int cnt=0;
if(depth[a]<depth[b]) swap(a,b);
for(int k=16;k>=0;k--)
{
if(depth[fa[a][k]]>=depth[b])
{
distance[cnt++]=d1[a][k];
distance[cnt++]=d2[a][k];
a=fa[a][k];
}
}
if(a!=b)
{
for(int k=16;k>=0;k--)
{
if(fa[a][k]!=fa[b][k])
{
distance[cnt++]=d1[a][k];
distance[cnt++]=d2[a][k];
distance[cnt++]=d1[b][k];
distance[cnt++]=d2[b][k];
a=fa[a][k],b=fa[b][k];
}
}
distance[cnt++]=d1[a][0];
distance[cnt++]=d1[b][0];
}
int dist1=-inf,dist2=-inf;
for(int i=0;i<cnt;i++)
{
int d=distance[i];
if(d>dist1) dist2=dist1,dist1=d;
else if (d!=dist1 && d>dist2) dist2=d;
}
if(w>dist1) return w-dist1;
if(w>dist2) return w-dist2;
return inf;
}
int main()
{
//freopen("test.in","r",stdin);//设置 cin scanf 这些输入流都从 test.in中读取
//freopen("test.out","w",stdout);//设置 cout printf 这些输出流都输出到 test.out里面去
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
edge[i]={a,b,c};
}
ll sum=kruskal();
build();
bfs();
ll res=1e18;
for(int i=0;i<m;i++)
{
if(!edge[i].used)
{
int a=edge[i].a,b=edge[i].b,w=edge[i].w;
res=min(res,sum+lca(a,b,w));
}
}
cout<<res<<endl;
return 0;
}