次小生成树
Posted wwhhtt
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了次小生成树相关的知识,希望对你有一定的参考价值。
试题描述
|
小C最近学了很多最小生成树的算法,Prim算法、 Kurskal算法、消圈算法等,正当小C
得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这次 生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选的 边集是Es,那么需要满足:( value(e)表示边e的权值) ∑ value(e)<∑ value(e) 这下小C蒙了,他找到了你,希望你帮他解决这个问题 |
输入
|
第一行包含两个整数N和M,表示无向图的点数与边数
接下来M行,每行3个数x,y,z,表示点x和点y之间有一条边,边的权值为2 |
输出
|
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证存在严格次小生成树)
|
输入示例
|
5 6
1 2 1 1 3 2 2 4 3 3 5 4 3 4 3 4 5 6 |
输出示例
|
11
|
按理来讲是一道挺菜的题
然而我们学校的OJ卡常比较严重
交了五十多变也没过,但是在Loj上可以
权且当做看个思想吧
我们知道既然是生成树,那么肯定加一条边后会形成环
然后我们再删掉环上与加进去的边权值不同的最长边,就得到了次小生成树
然后再所有的情况中选出一个最小的
其中因为数据范围的缘故,需要一个LCA来维护
下面给出代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<cmath> using namespace std; inline long long rd() { long long x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch==‘-‘) f=-1; for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-‘0‘; return x*f; } inline void write(long long x) { if(x<0) putchar(‘-‘),x=-x; if(x>9) write(x/10); putchar(x%10+‘0‘); return ; } int n,m; struct node{ int u,v; long long w; }s[600006]; bool cmp(const node x,const node y){ return x.w<y.w; } int f[300006]; int head[300006],to[600006],nxt[600006],dis[600006]; int fa[300006][30]; int dep[300006]; long long maxn[300006][30]; long long maxm[300006][30]; int vis[300006]; int total=0; void add(int x,int y,long long z){ total++; to[total]=y; dis[total]=z; nxt[total]=head[x]; head[x]=total; return ; } int getf(int v){ if(v==f[v]) return v; return f[v]=getf(f[v]); } void dfs(int x,int la){ dep[x]=dep[la]+1; for(register int e=head[x];e;e=nxt[e]){ if(to[e]!=la){ fa[to[e]][0]=x; maxn[to[e]][0]=dis[e]; maxm[to[e]][0]=-0x7fffffff; dfs(to[e],x); } } return ; } int LCA(int x,int y,long long w){ if(dep[x]<dep[y]) swap(x,y); long long maxs=-0x7fffffff; int de=dep[x]-dep[y]; for(register int i=16;i>=0;i--){ if((1<<i)&de){ if(maxn[x][i]==w) maxs=maxs>maxm[x][i]?maxs:maxm[x][i]; else maxs=maxs>maxn[x][i]?maxs:maxn[x][i]; x=fa[x][i]; } } if(x==y){ if(maxs==-0x7fffffff) return 0; return w-maxs; } for(register int i=16;i>=0;i--){ if(fa[x][i]!=fa[y][i]){ if(maxn[x][i]==w) maxs=maxs>maxm[x][i]?maxs:maxm[x][i]; else maxs=maxs>maxn[x][i]?maxs:maxn[x][i]; if(maxn[y][i]==w) maxs=maxs>maxm[y][i]?maxs:maxm[y][i]; else maxs=maxs>maxn[y][i]?maxs:maxn[y][i]; x=fa[x][i]; y=fa[y][i]; } } if(maxn[x][0]==w) maxs=maxs>maxm[x][0]?maxs:maxm[x][0]; else maxs=maxs>maxn[x][0]?maxs:maxn[x][0]; if(maxn[y][0]==w) maxs=maxs>maxm[y][0]?maxs:maxm[y][0]; else maxs=maxs>maxn[y][0]?maxs:maxn[y][0]; if(maxs==-0x7fffffff) return 0; return w-maxs; } int main() { n=rd(); m=rd(); for(register int i=1;i<=m;i++){ s[i].u=rd(); s[i].v=rd(); s[i].w=rd(); } for(register int i=1;i<=n;i++) f[i]=i; sort(s+1,s+m+1,cmp); int cnt=0; long long sum=0; for(register int i=1;i<=m;i++){ int x=getf(s[i].v),y=getf(s[i].u); if(x!=y){ vis[i]=1; f[x]=y; cnt++; sum+=s[i].w; add(s[i].v,s[i].u,s[i].w); add(s[i].u,s[i].v,s[i].w); } if(cnt==n-1) break; } dfs(1,0); for(register int j=1;j<=16;j++){ for(int e=1;e<=n;e++){ fa[e][j]=fa[fa[e][j-1]][j-1]; maxn[e][j]=maxn[e][j-1]>maxn[fa[e][j-1]][j-1]?maxn[e][j-1]:maxn[fa[e][j-1]][j-1]; maxm[e][j]=maxm[e][j-1]>maxm[fa[e][j-1]][j-1]?maxm[e][j-1]:maxm[fa[e][j-1]][j-1]; if(maxn[e][j-1]>maxn[fa[e][j-1]][j-1]) maxm[e][j]=maxm[e][j]>maxn[fa[e][j-1]][j-1]?maxm[e][j]:maxn[fa[e][j-1]][j-1]; if(maxn[e][j-1]<maxn[fa[e][j-1]][j-1]) maxm[e][j]=maxm[e][j]>maxn[e][j-1]?maxm[e][j]:maxn[e][j-1]; } } long long ans=0x7fffffff; for(register int i=1;i<=m;i++){ if(vis[i]) continue; long long num=LCA(s[i].u,s[i].v,s[i].w); if(!num) continue; ans=min(ans,num); } write(ans+sum); return 0; }
以上是关于次小生成树的主要内容,如果未能解决你的问题,请参考以下文章