给定一张 N 个点 M 条边的无向图,求无向图的严格次小生成树。
设最小生成树的边权之和为sum,严格次小生成树就是指边权之和大于sum的生成树中最小的一个。
输入格式
第一行包含两个整数N和M。
接下来M行,每行包含三个整数x,y,z,表示点x和点y之前存在一条边,边的权值为z。
输出格式
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
数据范围
N≤105,M≤3∗105N≤105,M≤3∗105
输入样例:
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
输出样例:
11
思路
原理和"秘密的牛奶运输"一样。不过这道题的点数和边数大了一个级别,不能爆力枚举树上两点之间的最长边.
而是通过倍增lca预处理点到祖先节点的最长边,道理一样\(fa[u][i]\)表示节点u的第\(2^i\)个祖先,\(d1[u][i],d2[u][i]\)表示u到第\(2^i\)和祖先节点的最长和次长边的长度。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=100010,M=300010,INF=0x3f3f3f3f;
typedef long long LL;
int h[N],e[N*2],w[N*2],nex[N*2],idx,n,m;
struct edge{
int x,y,z;
bool used;
bool operator < (const edge & b) const {
return z < b.z;
}
}eg[M];
void add(int u,int v,int t){
e[idx]=v;
w[idx]=t;
nex[idx]=h[u];
h[u]=idx++;
}
int f[N];
int Find(int x){
return f[x]==x ? x:f[x]=Find(f[x]);
}
LL kruskal(){
sort(eg+1,eg+1+m);
LL sum=0;
memset(h,-1,sizeof h);
for(int i=1;i<=m;++i){
int x=eg[i].x,y=eg[i].y,t=eg[i].z;
int fx=Find(x),fy=Find(y);
if(fx!=fy){
f[fx]=fy;
eg[i].used=1;
add(x,y,t);
add(y,x,t);
sum+=t;
}
}
return sum;
}
int fa[N][20],d1[N][20],d2[N][20],q[N],dep[N];
void bfs(){
int hh=0,tt=0;
q[tt++]=1;
memset(dep,0x3f,sizeof dep);
dep[0]=0,dep[1]=1;
while(hh!=tt){
int u=q[hh++];
for(int i=h[u];~i;i=nex[i]){
int v=e[i],t=w[i];
if(dep[v]>dep[u]+1){
dep[v]=dep[u]+1;
q[tt++]=v;
fa[v][0]=u;
d1[v][0]=t,d2[v][0]=-INF;
for(int j=1;j<18;++j){
int anc=fa[v][j-1];
fa[v][j]=fa[anc][j-1];
int dis[4]={d1[v][j-1],d2[v][j-1],d1[anc][j-1],d2[anc][j-1]};
d1[v][j]=d2[v][j]=-INF;
for(int k=0;k<4;++k){
int d=dis[k];
if(d>d1[v][j]){
d2[v][j]=d1[v][j];
d1[v][j]=d;
}
else if(d!=d1[v][j]&&d>d2[v][j]){
d2[v][j]=d;
}
}
}
}
}
}
}
int lca(int x,int y,int w){
if(dep[x]<dep[y]) swap(x,y);
static int dis[N*2];
int cnt=0;
for(int i=17;i>=0;--i){
if(dep[fa[x][i]]>=dep[y]){
dis[cnt++]=d1[x][i];
dis[cnt++]=d2[x][i];
x=fa[x][i];
}
}
if(x!=y){
for(int i=17;i>=0;--i){
if(fa[x][i]!=fa[y][i]){
dis[cnt++]=d1[x][i];
dis[cnt++]=d2[x][i];
dis[cnt++]=d1[y][i];
dis[cnt++]=d2[y][i];
x=fa[x][i],y=fa[y][i];
}
}
dis[cnt++]=d1[x][0];
dis[cnt++]=d2[y][0];
}
int dist1,dist2;
dist1=dist2=-INF;
for(int i=0;i<cnt;++i){
int d=dis[i];
if(d>dist1) dist2=dist1,dist1=d;
else if(d!=dist1&&d>dist2) dist2=d;
}
if(w!=dist1) return dist1;
else return dist2;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) f[i]=i;
for(int i=1;i<=m;++i){
int x,y,t;
scanf("%d%d%d",&x,&y,&t);
eg[i]={x,y,t,0};
}
LL sum=kruskal();
bfs();
LL ans=1e18;
for(int i=1;i<=m;++i){
if(!eg[i].used){
int x=eg[i].x,y=eg[i].y,z=eg[i].z;
ans=min(ans,sum+z-lca(x,y,z));
}
}
cout<<ans<<endl;
return 0;
}