#3775 次小生成树
题面
给定一张 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
提示
对于全部数据,
1
≤
N
≤
1
0
5
,
1
≤
M
≤
3
×
1
0
5
1
≤
N
≤
1
0
5
,
1
≤
M
≤
3
×
1
0
5
,
1≤N≤10^5,1≤M≤3×10^51≤N≤10^5,1≤M≤3×10^5,
1≤N≤105,1≤M≤3×1051≤N≤105,1≤M≤3×105,数据中无向图无自环,边权值非负且不超过
1
0
9
10^9
109 。
SOL
对于一颗最小生成树,我们可以枚举树中的每条非树边并用这条边去尝试做出一些替换,但是光靠枚举效率很低,于是想到了LCA。
用LCA维护每条非树边两端点的LCA路径上的最大值和次大值(因为对于每一条非树边的两个端点,它的两个端点的树边上的边权最大值肯定小于等于这条非树边的权值,所以只需要考虑最大值和次大值)然后每次用非树边尝试替换,求出严格次小生成树。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
#define M 300005
using namespace std;
inline int rd(){
int register data=0,w=1;static char ch=0;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(isdigit(ch))data=(data<<1)+(data<<3)+ch-'0',ch=getchar();
return data*w;
}
inline void write(int x){if(x>9)write(x/10);putchar(x%10+'0');}
int n,m,sum,minn=0x7f7f7f7f7f;
int fa[N],f[N][20],fir[N][20],sec[N][20],dep[N];
bool used[M];
inline int find(int x){return fa[x]==x? x:fa[x]=find(fa[x]);}
struct node{int u,v,w,nxt;}e[M<<1],E[M];
int cnt,first[N];
inline void add(int u,int v,int w){e[++cnt].v=v;e[cnt].w=w;e[cnt].nxt=first[u];first[u]=cnt;}
inline bool cmp(node a,node b){return a.w<b.w;}
inline void dfs(int u){
for(int register i=1;(1<<i)<=dep[u];i++){
f[u][i]=f[f[u][i-1]][i-1];
fir[u][i]=max(fir[f[u][i-1]][i-1],fir[u][i-1]);
if(fir[u][i-1]!=fir[f[u][i-1]][i-1])sec[u][i]=min(fir[u][i-1],fir[f[u][i-1]][i-1]);
sec[u][i]=max(sec[u][i],max(sec[u][i-1],sec[f[u][i-1]][i-1]));
}
for(int register i=first[u];i;i=e[i].nxt){
int register v=e[i].v;
if(dep[v])continue;
dep[v]=dep[u]+1;
fir[v][0]=e[i].w;
f[v][0]=u;
dfs(v);
}
}
typedef pair<int,int> T;
#define mp make_pair
inline T lca(int a,int b){
if(dep[a]<dep[b])swap(a,b);
int register gap=dep[a]-dep[b],firs=0,seco=0;
for(int register i=0;(1<<i)<=gap;i++)
if(gap&(1<<i)){
if(firs!=fir[a][i])seco=max(seco,min(firs,fir[a][i]));
seco=max(seco,sec[a][i]);
firs=max(firs,fir[a][i]);
a=f[a][i];
}
if(a==b)return mp(firs,seco);
for(int register i=18;i>=0;i--){
if(f[a][i]!=f[b][i]){
if(firs!=fir[a][i])seco=max(seco,min(firs,fir[a][i]));
seco=max(seco,sec[a][i]);
firs=max(firs,fir[a][i]);
if(firs!=fir[b][i])seco=max(seco,min(firs,fir[b][i]));
seco=max(seco,sec[b][i]);
firs=max(firs,fir[b][i]);
a=f[a][i];b=f[b][i];
}
}
seco=max(seco,max(sec[b][0],sec[a][0]));
if(firs!=fir[a][0])seco=max(seco,min(firs,fir[a][0]));
if(firs!=fir[b][0])seco=max(seco,min(firs,fir[b][0]));
firs=max(firs,max(fir[a][0],fir[b][0]));
return mp(firs,seco);
}
signed main(){
n=rd();m=rd();
for(int register i=1;i<=n;i++)fa[i]=i;
for(int register i=1;i<=m;i++){E[i].u=rd();E[i].v=rd();E[i].w=rd();}
sort(E+1,E+m+1,cmp);
for(int register i=1;i<=m;i++){
int register u=E[i].u,v=E[i].v,w=E[i].w;
int register fu=find(u),fv=find(v);
if(fu!=fv){
sum+=w;
add(u,v,w);add(v,u,w);
fa[fu]=fv;used[i]=1;
}
}
dep[1]=1;dfs(1);
for(int register i=1;i<=m;i++){
if(used[i])continue;
int register u=E[i].u,v=E[i].v,w=E[i].w;
T tmp=lca(u,v);
if(tmp.first!=w)minn=min(minn,w-tmp.first);
else minn=min(minn,w-tmp.second);
}
write(sum+minn);return 0;
}