D141 次小生成树 Kruskal 算法+树上倍增 P4180 [BJWC2010] 严格次小生成树
D141 次小生成树 Kruskal 算法+树上倍增 P4180 [BJWC2010] 严格次小生成树_哔哩哔哩_bilibili
P4180 [BJWC2010] 严格次小生成树 - 洛谷 U592756 秘密的牛奶运输(次最小生成树弱化版) - 洛谷
求无向图的严格次小生成树的边权和。严格次小是比最小生成树的边权和要大。
思路
先求出最小生成树的边权和 $sum$
如果添加一条非树边 $(u,v,w)$,则形成一个环。设环上树边的最大边权为 $d1$,次大边权为 $d2$
如果 $w>d1$,则用非树边 $(u,v,w)$ 替换最大边 $d1$,得到候选答案 $sum-d1+w$
如果 $w=d1$,则用非树边 $(u,v,w)$ 替换次大边 $d2$,得到候选答案 $sum-d2+w$
枚举每条非树边,取候选答案的最小值
图中最小生成树为 10;非树边 6 可以替换树边 4,结果为 12;非树边 3 可以替换树边 2,结果为 11

如何快速找到一条路径上的最大边和次大边?
用树上倍增算法预处理:
$d1[u][i]$ 表示从 $u$ 点开始向上跳 $2^i$ 条边到达 $p$ 点,这条路径上的最大边权
$d2[u][i]$ 表示从 $u$ 点开始向上跳 $2^i$ 条边到达 $p$ 点,这条路径上的次大边权
$up$ 路径的中点 $o=fa[u][i-1]$,把路径拆成两段 $uo$ 和 $op$ 寻找
从 $\{d1[u][i-1],d1[o][i-1],d2[u][i-1],d2[o][i-1]\}$ 这 4 个数中排序求出 $d1[u][i],d2[u][i]$

相关板子:
D08【模板】最小生成树 Kruskal 算法 - 董晓 - 博客园
// 最小生成树 kruskal算法 O(M*logM) #include<bits/stdc++.h> #define ll long long using namespace std; const int N=100005,M=300005,INF=0x3f3f3f3f; int idx,h[N],to[M],ne[M],ww[M]; void add(int u,int v,int w){ //连边 to[++idx]=v,ww[idx]=w,ne[idx]=h[u],h[u]=idx; to[++idx]=u,ww[idx]=w,ne[idx]=h[v],h[v]=idx; } int n,m; ll sum; struct E{int u,v,w;}e[M]; //边集 bool used[M]; int fa[N]; //并查集的fa int find(int u){ //并查集的找根 return fa[u]==u?u:fa[u]=find(fa[u]); } void kruskal(){ int tot=0; for(int i=1; i<=n; i++) fa[i]=i; sort(e+1,e+m+1,[&](E u,E v){return u.w<v.w;}); for(int i=1; i<=m; i++){ int u=find(e[i].u),v=find(e[i].v); if(u!=v){ fa[u]=v; sum+=e[i].w; //累加边权和 used[i]=true; //记录树边 add(e[i].u,e[i].v,e[i].w); //建最小生成树 if(++tot==n-1) break; } } } struct Tree{ int fa[N][18],dep[N]; int d1[N][18]; //d1[u][i]表示从u点开始向上跳2^i条边,这条路径上的最大边权 int d2[N][18]; //d2[u][i]表示从u点开始向上跳2^i条边,这条路径上的次大边权,不存在为-INF void dfs(int u,int f){ //预处理fa,d1,d2数组 dep[u]=dep[f]+1; fa[u][0]=f; d2[u][0]=-INF; for(int i=1; i<=17; i++){ fa[u][i]=fa[fa[u][i-1]][i-1]; int d[4]={d1[u][i-1],d1[fa[u][i-1]][i-1],d2[u][i-1],d2[fa[u][i-1]][i-1]}; sort(d,d+4); d1[u][i]=d[3]; //最大边权 int p=2; while(p>=0 && d[p]==d[3]) p--; d2[u][i]=(p==-1?-INF:d[p]); //次大边权 } for(int i=h[u]; i; i=ne[i]){ int v=to[i],w=ww[i]; if(v!=f){ d1[v][0]=w; dfs(v,u); } } } int lca(int u,int v){ //倍增求lca if(dep[u]<dep[v]) swap(u,v); for(int i=17;i>=0;i--)if(dep[fa[u][i]]>=dep[v]) u=fa[u][i]; if(u==v) return u; for(int i=17; i>=0; i--)if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; return fa[u][0]; } int query(int u,int v,int w){ //倍增求小于w的最大边权 int res=-INF; for(int i=17; i>=0; i--){ if(dep[fa[u][i]]>=dep[v]){ if(w>d1[u][i]) res=max(res,d1[u][i]); else if(w==d1[u][i]) res=max(res,d2[u][i]); u=fa[u][i]; } } return res; } }T; int main(){ scanf("%d%d",&n,&m); for(int i=1,u,v,w; i<=m; i++){ scanf("%d%d%d",&u,&v,&w); e[i]={u,v,w}; } kruskal(); T.dfs(1,0); ll ans=1e18; for(int i=1; i<=m; i++)if(!used[i]){ //非树边 auto [u,v,w]=e[i]; int l=T.lca(u,v); ll w1=T.query(u,l,w),w2=T.query(v,l,w); ans=min(ans,sum-max(w1,w2)+w); } printf("%lld\n",ans); }
浙公网安备 33010602011771号