【BZOJ 1977】 [BeiJing2010组队]次小生成树 Tree

来源:互联网 发布:开票软件凭证接口 编辑:程序博客网 时间:2024/05/29 19:38

1977: [BeiJing2010组队]次小生成树 Tree

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 2313  Solved: 544
[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

Sample Output

数据中无向图无自环; 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;}


1 0