一本通1555【例 4】次小生成树
1555:【例 4】次小生成树
时间限制: 1000 ms 内存限制: 524288 KB
题目描述
原题来自:BeiJing 2010 组队赛
给定一张 N 个点 M 条边的无向图,求无向图的严格次小生成树。
设最小生成树的边权之和为 sum,严格次小生成树就是指边权之和大于 sum 的生成树中最小的一个。
输入格式
第一行包含两个整数 N 和 M,表示无向图的点数与边数;
接下来 MM 行,每行三个数 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≤10^5,1≤M≤3×10^5,数据中无向图无自环,边权值非负且不超过 10^9。
sol:严格次小生成树模板,我使用倍增做的,树剖也可以(表示不清楚别人为什么这么快)
#include <bits/stdc++.h> using namespace std; const int N=100005,M=300005,inf=0x3f3f3f3f; int n,m; struct Edge { int U,V,Val; }E[M]; inline bool cmp(Edge p,Edge q) { return p.Val<q.Val; } int Father[N]; long long MST=0; bool Edge_Bo[M]; inline int Get_Father(int x) { return (Father[x]==x)?(x):(Father[x]=Get_Father(Father[x])); } struct Tree { int tot,Next[M],to[M],head[N],val[M]; inline void add(int x,int y,int z) { Next[++tot]=head[x]; to[tot]=y; val[tot]=z; head[x]=tot; return; } int Depth[N],F[N][23]; int Val_Zd[N][23],Val_Cd[N][23]; inline void dfs(int x,int fa) { F[x][0]=fa; Depth[x]=Depth[fa]+1; int i; for(i=head[x];i;i=Next[i]) if(to[i]!=fa) { Val_Zd[to[i]][0]=val[i]; Val_Cd[to[i]][0]=-inf; dfs(to[i],x); } return; } inline void Pre() { int i,j; dfs(1,0); for(i=1;i<=19;i++) { for(j=1;j<=n;j++) { F[j][i]=F[F[j][i-1]][i-1]; Val_Zd[j][i]=max(Val_Zd[j][i-1],Val_Zd[F[j][i-1]][i-1]); Val_Cd[j][i]=max(Val_Cd[j][i-1],Val_Cd[F[j][i-1]][i-1]); if(Val_Zd[j][i-1]<Val_Zd[F[j][i-1]][i-1]) Val_Cd[j][i]=max(Val_Cd[j][i],Val_Zd[j][i-1]); else if(Val_Zd[F[j][i-1]][i-1]<Val_Zd[j][i-1]) Val_Cd[j][i]=max(Val_Cd[j][i],Val_Zd[F[j][i-1]][i-1]); } } return; } inline int Ask_Lca(int x,int y) { int i; if(Depth[x]<Depth[y]) swap(x,y); for(i=19;~i;i--) if(Depth[F[x][i]]>=Depth[y]) x=F[x][i]; if(x==y) return x; for(i=19;~i;i--) if(F[x][i]!=F[y][i]) x=F[x][i],y=F[y][i]; return F[x][0]; } inline int Ask_Lower(int x,int y,int z) { int i,Now=x,ans=0; for(i=19;~i;i--) if(Depth[F[Now][i]]>=Depth[y]) { // printf("Now=%d Val_Zd[Now][i]=%d BB=%d\n",Now,Val_Zd[Now][i],z); // printf("Now=%d Val_Cd[Now][i]=%d BB=%d\n",Now,Val_Cd[Now][i],z); if(Val_Zd[Now][i]<z) ans=max(ans,Val_Zd[Now][i]); else ans=max(ans,Val_Cd[Now][i]); Now=F[Now][i]; } return ans; } }T; int main() { int i; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) Father[i]=i; for(i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); E[i]=(Edge){x,y,z}; } sort(E+1,E+m+1,cmp); for(i=1;i<=m;i++) { int xx=Get_Father(E[i].U),yy=Get_Father(E[i].V); if(xx==yy) continue; Father[xx]=yy; MST+=E[i].Val; Edge_Bo[i]=1; T.add(E[i].U,E[i].V,E[i].Val); T.add(E[i].V,E[i].U,E[i].Val); } // printf("MST=%d\n",MST); T.Pre(); long long ans=0x7ffffffffff; for(i=1;i<=m;i++) if(!Edge_Bo[i]) { int x=E[i].U,y=E[i].V,z=E[i].Val; int ll=T.Ask_Lca(x,y); // printf("x=%d y=%d ll=%d\n",x,y,ll); int p1=T.Ask_Lower(x,ll,z),p2=T.Ask_Lower(y,ll,z); // printf("p1=%d p2=%d\n",p1,p2); ans=min(ans,MST-max(p1,p2)+z); } printf("%lld\n",ans); return 0; } /* input 5 6 1 2 1 1 3 2 2 4 3 3 5 4 3 4 3 4 5 6 output 11 */
河田は河田、赤木は赤木……。
私は誰ですか。教えてください、私は誰ですか。
そうだ、俺はあきらめない男、三井寿だ!