356. 次小生成树

题目链接

356. 次小生成树

给定一张 N 个点 M 条边的无向图,求无向图的严格次小生成树。

设最小生成树的边权之和为 sum,严格次小生成树就是指边权之和大于 sum 的生成树中最小的一个。

输入格式

第一行包含两个整数 NM

接下来 M 行,每行包含三个整数 xyz,表示点 x 和点 y 之前存在一条边,边的权值为 z

输出格式

包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

数据范围

N105,M3×105

输入样例:

5 6 1 2 1 1 3 2 2 4 3 3 5 4 3 4 3 4 5 6

输出样例:

11

解题思路

倍增,最小生成树

先求出最小生成树,最小生成树中的边称为“树边”,对于每一条“非树边”,计算加上这条边后的最少代价,即加上这条边后会形成一个环,当权值最大的边的权值与当当前“非树边”的权值不相等时,需要在环去掉这条权值最大的边,否则去掉权值次大的边。所以关键在于寻找最小生成树中两点之间权值最大和次大的边,可类比LCA:

  • 状态表示:dp[i][j][k] 表示 j 向上走 2k 步的最大值(i=0)/次大值(i=1

  • 状态计算:

  • dp[0][y][i1]==dp[0][f[y][i1]][i1],其中 f[i][j] 表示 i 向上走 2j 步到达的节点

    • dp[0][y][i]=dp[0][y][i1]
    • dp[1][y][i]=max(dp[1][y][i1],dp[1][f[y][i1]][i1])
  • dp[0][y][i1]dp[0][f[y][i1]][i1]

    • dp[0][y][i]=max(dp[0][y][i1],dp[0][f[y][i1]][i1])
    • dp[1][y][i]=max({min(dp[0][y][i1],dp[0][f[y][i1]][i1]),dp[1][y][i1],dp[1][f[y][i1]][i1]})
      分析:整体最大值为前后两部分的最大值,当前后两部分的最大值相等时,次大值为前后两部分次大值的较大值;否则为前后两部分最大值的较小值和前后两部分的次大值的最大值

最后求最小生成树两点的最大和次大值可类比LCA

  • 时间复杂度:O(m(logn+logm))

代码

// Problem: 次小生成树 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/358/ // Memory Limit: 512 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> // #define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=3e5+5; const LL inf=1e16; int n,m,fa[N],d[N],f[N][20]; LL dp[2][N][20],val1,val2,res,ret; bool v[N]; vector<PII> adj[N]; struct T { int x,y,z; bool operator<(const T &t) { return z<t.z; } }tr[N]; int find(int x) { return x==fa[x]?x:fa[x]=find(fa[x]); } void kruskal() { sort(tr+1,tr+1+m); for(int i=1;i<=m;i++) { int x=tr[i].x,y=tr[i].y,z=tr[i].z; int a=find(x),b=find(y); if(a==b)continue; res+=z; adj[x].pb({y,z}); adj[y].pb({x,z}); v[i]=true; fa[a]=b; } } void bfs() { d[1]=0; queue<int> q; q.push(1); while(q.size()) { int x=q.front(); q.pop(); int len=log2(d[x]); for(auto t:adj[x]) { int y=t.fi,z=t.se; if(y==f[x][0])continue; q.push(y); f[y][0]=x; d[y]=d[x]+1; dp[0][y][0]=z,dp[1][y][0]=-inf; for(int i=1;i<=len;i++) { f[y][i]=f[f[y][i-1]][i-1]; if(dp[0][y][i-1]==dp[0][f[y][i-1]][i-1]) { dp[0][y][i]=dp[0][y][i-1]; dp[1][y][i]=max(dp[1][y][i-1],dp[1][f[y][i-1]][i-1]); } else { dp[0][y][i]=max(dp[0][y][i-1],dp[0][f[y][i-1]][i-1]); dp[1][y][i]=max({min(dp[0][y][i-1],dp[0][f[y][i-1]][i-1]),dp[1][y][i-1],dp[1][f[y][i-1]][i-1]}); } } } } } void update(int x) { if(val1<x)val2=val1,val1=x; else if(val2<x&&x!=val1)val2=x; } void lca(int x,int y) { val1=val2=-inf; if(d[x]>d[y])swap(x,y); while(d[x]<d[y]) { int len=log2(d[y]-d[x]); update(dp[0][y][len]); update(dp[1][y][len]); y=f[y][len]; } if(x==y)return ; for(int len=log2(d[x]);len>=0;len--) if(f[x][len]!=f[y][len]) { update(dp[0][x][len]); update(dp[1][x][len]); update(dp[0][y][len]); update(dp[1][y][len]); x=f[x][len]; y=f[y][len]; } update(dp[0][x][0]); update(dp[1][x][0]); update(dp[0][y][0]); update(dp[1][y][0]); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); tr[i]={x,y,z}; } kruskal(); bfs(); ret=inf; for(int i=1;i<=m;i++) { if(v[i])continue; lca(tr[i].x,tr[i].y); if(tr[i].z==val1) ret=min(ret,res-val2+tr[i].z); else ret=min(ret,res-val1+tr[i].z); } printf("%lld",ret); return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16039099.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示