次小生成树
题目描述
小 C
最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C
洋洋得意之时,小 P
又来泼小 C
冷水了。小 P
说,让小 C
求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 E_MEM ,严格次小生成树选择的边集是 E_SES ,那么需要满足:\sum _{e \in E_S} value(e) < \sum _{e \in E_M} value(e)∑e∈ESvalue(e)<∑e∈EMvalue(e) 。
这下小 C
蒙了,他找到了你,希望你帮他解决这个问题。
输入格式
第一行包含两个整数 nn 和 mm ,表示无向图的点数与边数。
接下来 mm 行,每行3个数 x,y,zx,y,z 表示,点 xx 和点 yy 之间有一条边,边的权值为 zz 。
输出格式
包含一行,仅一个数,表示严格次小生成树的边权和。
solution
次小生成树与最小生成树一定只差一条边。
如果差两条,我们一定可以把一条换回去。
那么就先求出最小生成树,枚举非树边,找它在树上连成的环上的最小值替代即可。
由于要严格次小,所以还得维护次小值,分讨即可。
#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define maxn 500005 using namespace std; int n,m,head[maxn],tot,fl[maxn]; int par[maxn],deep[maxn]; long long ans; struct node{ int u,v,w,nex; }e[maxn*2],E[maxn]; struct no{ int f,m1,m2; }st[maxn][22]; bool cmp(node a,node b){ return a.w<b.w; } int getf(int k){return par[k]==k?k:par[k]=getf(par[k]);} void lj(int t1,int t2,int t3){ e[++tot].v=t2;e[tot].w=t3;e[tot].nex=head[t1];head[t1]=tot; } void Kruskal(){ sort(E+1,E+m+1,cmp); for(int i=1;i<=n;i++)par[i]=i; int sum=0; for(int i=1;i<=m;i++){ int f1=getf(E[i].u),f2=getf(E[i].v); if(f1!=f2){ fl[i]=1; lj(E[i].u,E[i].v,E[i].w); lj(E[i].v,E[i].u,E[i].w); ans+=E[i].w; par[f1]=f2;sum++;if(sum==n-1)break; } } } void dfs(int k,int fa){ st[k][0].f=fa;deep[k]=deep[fa]+1; for(int i=head[k];i;i=e[i].nex){ if(e[i].v!=fa){ st[e[i].v][0].m1=e[i].w; dfs(e[i].v,k); } } } no merge(no A,no B){ no C;C.f=B.f; C.m1=max(A.m1,B.m1); C.m2=max(A.m2,B.m2); if(A.m1<C.m1)C.m2=max(C.m2,A.m1); if(B.m1<C.m1)C.m2=max(C.m2,B.m1); return C; } no get(int v,int u){ no A;A.m1=A.m2=0; if(deep[u]<deep[v])swap(u,v); for(int x=20;x>=0;x--){ if(deep[st[u][x].f]>=deep[v]){ A=merge(A,st[u][x]); u=st[u][x].f; } } for(int x=20;x>=0;x--){ if(st[u][x].f!=st[v][x].f){ A=merge(A,st[u][x]);A=merge(A,st[v][x]); u=st[u][x].f,v=st[v][x].f; } } if(u==v)return A; A=merge(A,st[u][0]);A=merge(A,st[v][0]); return A; } int main(){ cin>>n>>m; for(int i=1;i<=m;i++){ scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w); } Kruskal(); dfs(1,0); for(int j=1;j<=20;j++) for(int i=1;i<=n;i++){ st[i][j]=merge(st[i][j-1],st[st[i][j-1].f][j-1]); } //for(int i=1;i<=n;i++)printf("%d %d %d\n",st[i][1].f,st[i][1].m1,st[i][1].m2);puts(""); int M=1e9; for(int i=1;i<=m;i++){ if(fl[i])continue; int u=E[i].u,v=E[i].v; no tmp=get(u,v); if(tmp.m1<E[i].w)M=min(M,E[i].w-tmp.m1); else if(tmp.m1==E[i].w)M=min(M,E[i].w-tmp.m2); } cout<<ans+M<<endl; return 0; }