题目描述
原题来自:BeiJing 2010 组队赛
给定一张 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
#include<bits/stdc++.h> const int MAXN=100005,MAXM=300005; using namespace std; int n,m,N; struct E{ int u,v,f; long long val; }a[MAXM*2]; bool mw(E a,E b) { return a.val<b.val; } struct Edge{ int to,next; long long val; }edge[MAXM*2]; int head[MAXN],num=0; int fa[MAXN],find(); long long ans,mn=100000000000; int f[MAXN][25],dep[MAXN]; long long d1[MAXN][25],d2[MAXN][25]; inline void add(int u,int v,long long val) { edge[++num].next=head[u]; edge[num].to=v; edge[num].val=val; head[u]=num; return; } inline int find(int a) { return a==fa[a]?a:fa[a]=find(fa[a]); } inline void grow() { int u,v,val,cnt=0; sort(a+1,a+m+1,mw); int r1,r2; for(register int i=1;i<=n;++i)fa[i]=i; for(register int i=1;i<=m;++i) { u=a[i].u,v=a[i].v,val=a[i].val; r1=find(u); r2=find(v); if(r1!=r2) { cnt++; a[i].f=1; add(u,v,val); add(v,u,val); fa[r1]=r2; ans+=val; if(cnt==n-1) break; } } return; } inline void dfs(int u) { for(register int i=1;i<=N;i++) { f[u][i]=f[f[u][i-1]][i-1]; d1[u][i]=max(d1[u][i-1],d1[f[u][i-1]][i-1]); if(d1[u][i-1]==d1[f[u][i-1]][i-1]) d2[u][i]=max(d2[u][i-1],d2[f[u][i-1]][i-1]); else { d2[u][i]=min(d1[u][i-1],d1[f[u][i-1]][i-1]); d2[u][i]=max(d2[u][i-1],d2[u][i]); d2[u][i]=max(d2[u][i],d2[f[u][i-1]][i-1]); } } for(register int i=head[u];i;i=edge[i].next) { int v=edge[i].to; if(v==f[u][0])continue; f[v][0]=u; d1[v][0]=edge[i].val; dep[v]=dep[u]+1; dfs(v); } return; } inline int lca(int a,int b) { if(dep[a]>dep[b]) swap(a,b); for(register int i=N;i>=0;i--) { if(dep[a]<dep[b]&&dep[f[b][i]]>=dep[a]) b=f[b][i]; } if(a==b)return a; for(register int i=N;i>=0;i--) { if(f[a][i]!=f[b][i]){a=f[a][i],b=f[b][i];} } return f[a][0]; } inline void Try(int u,int father,long long val) { int t; t=dep[u]-dep[father]; long long mx1=-1,mx2=0; for(int i=0;i<=N;i++) { if(t&(1<<i))//注意这一步!! { if(mx1<=d1[u][i]) { mx2=mx1; mx1=d1[u][i]; mx2=max(mx2,d2[u][i]); } else { mx2=max(mx2,d1[u][i]); } u=f[u][i];//写的好神奇 } } if(mx1!=val) { if(mn>val-mx1) { mn=val-mx1; } } else { if(mn>val-mx2) { mn=val-mx2; } } return; } inline void choose() { int f,u,v,val; for(register int i=1;i<=m;++i) { if(a[i].f==1) continue; u=a[i].u; v=a[i].v; val=a[i].val; f=lca(u,v); Try(u,f,val),Try(v,f,val); } return; } int main() { scanf("%d%d",&n,&m); for(register int i=1;i<=m;++i) { scanf("%d%d%lld",&a[i].u,&a[i].v,&a[i].val); } grow(); N=floor(log(n+0.0)/log(2.0)); dfs(1); choose(); printf("%lld",ans+mn); return 0; }