1977: [BeiJing2010组队]次小生成树 Tree
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 3001 Solved: 751
[Submit][Status][Discuss]
Description
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
Input
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
Output
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
11
HINT
数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。
Source
首先我们要知道次小生成树是最小生成树删掉一条边并加上一条边,那么枚举每条边,通过倍增计算最小边和次小边,计算答案即可。
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; typedef long long ll; typedef pair<ll,ll> PII; #define N 100010 struct edge { int to,nxt,w; }e[N*6]; struct edge1 { int u,v,w; }; vector<edge1> E; int n,m,cnt=1; ll tot; int head[N],father[N],dep[N],mark[N]; int fa[N][23]; ll maxcost[N][23]; ll max(ll x,ll y) { return x>y?x:y; } ll min(ll x,ll y) { return x<y?x:y; } bool cp(edge1 x,edge1 y) { return x.w<y.w; } void link(int u,int v,int w) { e[++cnt].nxt=head[u]; head[u]=cnt; e[cnt].to=v; e[cnt].w=w; } int find(int u) { return u==father[u]?father[u]:find(father[u]); } void connect(int u,int v) { int a=find(u); int b=find(v); if(a==b) return; father[a]=b; } void dfs(int u,int Fa) { for(int i=head[u];i;i=e[i].nxt) if(e[i].to!=Fa) { int v=e[i].to; dep[v]=dep[u]+1; fa[v][0]=u; maxcost[v][0]=e[i].w; dfs(v,u); } } PII lca(int u,int v) { PII ret; if(dep[u]<dep[v]) swap(u,v); for(int i=22;i>=0;i--) if((dep[u]-dep[v])&(1<<i)) { if(maxcost[u][i]>ret.first) { ret.second=ret.first; ret.first=maxcost[u][i]; } else if(maxcost[u][i]<ret.first) ret.second=max(ret.second,maxcost[u][i]); u=fa[u][i]; } if(u==v) return ret; for(int i=22;i>=0;i--) if(fa[u][i]!=fa[v][i]) { if(maxcost[u][i]>ret.first) { ret.second=ret.first; ret.first=maxcost[u][i]; } else if(maxcost[u][i]<ret.first) ret.second=max(ret.second,maxcost[u][i]); if(maxcost[v][i]>ret.first) { ret.second=ret.first; ret.first=maxcost[v][i]; } else if(maxcost[v][i]<ret.first) ret.second=max(ret.second,maxcost[v][i]); u=fa[u][i]; v=fa[v][i]; } if(maxcost[u][0]>ret.first) { ret.second=ret.first; ret.first=maxcost[u][0]; } else if(maxcost[u][0]<ret.first) ret.second=max(ret.second,maxcost[u][0]); if(maxcost[v][0]>ret.first) { ret.second=ret.first; ret.first=maxcost[v][0]; } else if(maxcost[v][0]<ret.first) ret.second=max(ret.second,maxcost[v][0]); return ret; } void build() { for(int i=1;i<=22;i++) for(int j=1;j<=n;j++) if(fa[j][i-1]!=-1) { fa[j][i]=fa[fa[j][i-1]][i-1]; maxcost[j][i]=max(maxcost[j][i-1], maxcost[fa[j][i-1]][i-1]); } } void kruskal() { sort(E.begin(),E.end(),cp); for(int i=1;i<=n;i++) father[i]=i; for(int i=0;i<E.size();i++) if(find(E[i].u)!=find(E[i].v)) { connect(E[i].u,E[i].v); mark[i]=1; tot+=E[i].w; link(E[i].u,E[i].v,E[i].w); link(E[i].v,E[i].u,E[i].w); } dfs(1,1); build(); ll ans=(ll)(1e17); for(int i=0;i<E.size();i++) if(!mark[i]) { PII x=lca(E[i].u,E[i].v); if(x.first<E[i].w) ans=min(ans,tot+E[i].w-x.first); else if(x.first==E[i].w) ans=min(ans,tot+E[i].w-x.second); } printf("%lld",ans); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); edge1 x; x.u=u; x.v=v; x.w=w; E.push_back(x); } kruskal(); return 0; }