小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。
倍增。
求出最小生成树,枚举每一条非树边,用它取代掉环上最大的边最优。
那么相当于求两点到lca的最大边权,直接倍增做。
但是要注意是严格最小生成树,那么这条非树边与环上最大边相同时可能取代掉的是环上的次大边,那么倍增的时候多维护一个严格次大的边即可。
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <cstring> #define LL long long #define pa pair<int,int> #define inf 1000000005 #define M 300000+5 #define N 100000+5 #include <queue> using namespace std; queue<int> q; LL pre=0; int a[5],fa[N],v[M],n,m,tot,h[N],d[N],f[N][21]; struct Edge { int x,y,v; }E[M]; struct edge { int y,ne,v; }e[M*2]; struct ma { int a,b; }g[N][21],p; bool cmp(Edge a,Edge b) { return a.v<b.v; } int Getfather(int x) { return x==fa[x]?x:fa[x]=Getfather(fa[x]); } void Kruscal() { sort(E+1,E+1+m,cmp); int cnt=1; for (int i=1;i<=m;i++) { int fx=Getfather(E[i].x),fy=Getfather(E[i].y); if (fx==fy) continue; cnt++; v[i]=1; fa[fx]=fy; pre+=E[i].v; } } void Addedge(int x,int y,int v) { e[++tot].y=y; e[tot].ne=h[x]; e[tot].v=v; h[x]=tot; e[++tot].y=x; e[tot].ne=h[y]; e[tot].v=v; h[y]=tot; } void Build() { for (int i=1;i<=m;i++) if (v[i]) Addedge(E[i].x,E[i].y,E[i].v); g[1][0].a=g[1][0].b=-inf; f[1][0]=0; d[1]=1; q.push(1); while (!q.empty()) { int x=q.front(); q.pop(); for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (y==f[x][0]) continue; f[y][0]=x; d[y]=d[x]+1; g[y][0].a=e[i].v; q.push(y); } } } void Update(ma &x,ma y) { /*a[0]=x.a,a[1]=x.b,a[2]=y.a,a[3]=y.b; sort(a,a+4); x.a=max(x.a,y.a); for (int k=2;k>=0;k--) if (a[k]!=a[k+1]) { x.b=a[k]; break; }*/ if (x.a>y.a) x.b=max(y.a,x.b); else if (x.a<y.a) x.b=max(x.a,y.b); else x.b=max(x.b,y.b); x.a=max(x.a,y.a); } void ST() { for (int j=1;(1<<j)<=n;j++) for (int i=1;i<=n;i++) { f[i][j]=f[f[i][j-1]][j-1]; Update(g[i][j],g[i][j-1]); Update(g[i][j],g[f[i][j-1]][j-1]); } } void Move(int &x,int deep) { for (int i=20;i>=0;i--) if (d[f[x][i]]>=deep) Update(p,g[x][i]),x=f[x][i]; } void Getlca(int x,int y) { p.a=p.b=-inf; if (d[x]>d[y]) swap(x,y); Move(y,d[x]); if (x==y) return; for (int i=20;i>=0;i--) if (f[x][i]!=f[y][i]) { Update(p,g[x][i]),Update(p,g[y][i]); x=f[x][i],y=f[y][i]; } Update(p,g[x][0]),Update(p,g[y][0]); } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) scanf("%d%d%d",&E[i].x,&E[i].y,&E[i].v); for (int i=1;i<=n;i++) fa[i]=i; Kruscal(); Build(); ST(); LL ans=(LL)1e15; for (int i=1;i<=m;i++) if (!v[i]) { Getlca(E[i].x,E[i].y); if (p.a==E[i].v) ans=min(ans,pre+E[i].v-p.b); else ans=min(ans,pre+E[i].v-p.a); } printf("%lld\n",ans); return 0; }