AcWing 356. 次小生成树
2022/7/10 23:54:51
本文主要是介绍AcWing 356. 次小生成树,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
分析
这题做法很简单:
-
跑一遍 \(\texttt{MST}\)(最小生成树),把这棵树建立起来,上面的边标记为树边。
-
枚举非树边 \((u, v)\),记边权为 \(w\),考虑这条边能够提供的增量 \(del\)。
具体来说:只需要求出树上 \(u\to v\) 的路径上的边的最大值 \(mx_1\) 和严格次大值 \(mx_2\)(也就是保证 \(mx_2 < mx_1\)),假如 \(w>mx_1\),那我们就更新 \(del\to \min(del, w-mx_1)\),如果 \(w=mx_1\),就更新 \(del\to \min(del, w-mx_2)\).
上面的思路简单来说就是枚举非树边,加入满足替换后增量最小的边以得到答案。
实现
关键在于使用什么样的数据结构维护树上路径的最大值与次大值。
这里采用了线段树 + 树链剖分:
线段树的每个节点维护了一个最大值与次大值,这两个信息都是可以合并的,\(\texttt{merge}\) 函数见下:
Node merge(Node &L, Node &R){ int rec[]={L.mx1, L.mx2, R.mx1, R.mx2}; int mx1=-INF, mx2=-INF; for(auto i: rec) mx1=max(mx1, i); rep(i,0,3) if(rec[i]!=mx1) mx2=max(mx2, rec[i]); return {L.l, R.r, mx1, mx2}; }
然后我们可以求出树上 \(u\to v\) 路径上的 \(LCA\),记为 \(a\)。
那么只需要对路径 \(u\to a\) 以及 \(v\to a\) 分别询问一次得到两条树链的信息(最大值、次大值)再合并这两个信息即可。
全部代码:
// Problem: 次小生成树 // Contest: AcWing // URL: https://www.acwing.com/problem/content/358/ // Memory Limit: 512 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #pragma GCC optimize("O3") #include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<vector> using namespace std; #define debug(x) cerr << #x << ": " << (x) << endl #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define dwn(i,a,b) for(int i=(a);i>=(b);i--) #define pb push_back #define all(x) (x).begin(), (x).end() #define x first #define y second using pii = pair<int, int>; using ll = long long; inline void read(int &x){ int s=0; x=1; char ch=getchar(); while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();} while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar(); x*=s; } const int N=1e5+5, M=1e6+5, INF=1e9+10; int n, m; struct Msg{ int u, v, w, vis; }buf[M]; struct Edge{ int to, w, next; }e[M]; int h[N], tot; void add(int u, int v, int w){ e[tot].to=v, e[tot].w=w, e[tot].next=h[u], h[u]=tot++; } int f[N]; int find(int x){ return x==f[x]? x: f[x]=find(f[x]); } ll MST(){ ll res=0; rep(i,1,n) f[i]=i; rep(i,1,m){ int u=buf[i].u, v=buf[i].v, w=buf[i].w; int pu=find(u), pv=find(v); if(pu!=pv) f[pu]=pv, res+=w, add(u, v, w), add(v, u, w), buf[i].vis=true; } return res; } int fa[N], son[N], sz[N], dep[N]; int W[N], val[N]; int top[N], dfn[N], ts; void dfs1(int u, int f, int d){ fa[u]=f, sz[u]=1, dep[u]=d; for(int i=h[u]; ~i; i=e[i].next){ int go=e[i].to; if(go==f) continue; W[go]=e[i].w; dfs1(go, u, d+1); sz[u]+=sz[go]; if(sz[son[u]]<sz[go]) son[u]=go; } } void dfs2(int u, int t){ top[u]=t, dfn[u]=++ts, val[ts]=W[u]; if(!son[u]) return; dfs2(son[u], t); for(int i=h[u]; ~i; i=e[i].next){ int go=e[i].to; if(go==fa[u] || go==son[u]) continue; dfs2(go, go); } } struct Node{ int l, r; int mx1, mx2; #define ls u<<1 #define rs u<<1|1 }tr[N<<2]; Node merge(Node &L, Node &R){ int rec[]={L.mx1, L.mx2, R.mx1, R.mx2}; int mx1=-INF, mx2=-INF; for(auto i: rec) mx1=max(mx1, i); rep(i,0,3) if(rec[i]!=mx1) mx2=max(mx2, rec[i]); return {L.l, R.r, mx1, mx2}; } void build(int u, int l, int r){ tr[u]={l, r}; if(l==r) return tr[u].mx1=val[l], tr[u].mx2=-INF, void(); int mid=l+r>>1; build(ls, l, mid), build(rs, mid+1, r); tr[u]=merge(tr[ls], tr[rs]); } void upd(int u, int x, int fir=-INF, int sec=-INF){ if(tr[u].l==tr[u].r){ tr[u].mx1=fir, tr[u].mx2=sec; return; } int mid=tr[u].l+tr[u].r>>1; if(x<=mid) upd(ls, x, fir, sec); else upd(rs, x, fir, sec); tr[u]=merge(tr[ls], tr[rs]); } Node query(int u, int l, int r){ if(l<=tr[u].l && tr[u].r<=r) return tr[u]; int mid=tr[u].l+tr[u].r>>1; Node L={-1}, R={-1}; if(l<=mid) L=query(ls, l, r); if(mid<r) R=query(rs, l, r); if(L.l==-1) return R; if(R.l==-1) return L; return merge(L, R); } Node query(int u, int v){ Node res={-1}, tmp; while(top[u]!=top[v]){ int t=top[u]; if(~res.l) res=merge((tmp=query(1, dfn[t], dfn[u])), res); else res=query(1, dfn[t], dfn[u]); u=fa[top[u]]; } if(~res.l) res=merge((tmp=query(1, dfn[v], dfn[u])), res); else res=query(1, dfn[v], dfn[u]); return res; } int lca(int u, int v){ while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]) swap(u, v); u=fa[top[u]]; } return dep[u]<dep[v]? u: v; } signed main(){ memset(h, -1, sizeof h); cin>>n>>m; rep(i,1,m){ int u, v, w; read(u), read(v), read(w); buf[i]={u, v, w, 0}; } sort(buf+1, buf+1+m, [&](const Msg &a, const Msg &b){ return a.w<b.w; }); ll res=MST(); dfs1(1, -1, 1), dfs2(1, 1); build(1, 1, n); int del=INF; rep(i,1,m) if(!buf[i].vis){ int u=buf[i].u, v=buf[i].v, w=buf[i].w; if(u==v) continue; int a=lca(u, v); Node L={-1}, R={-1}, res, rec; rec=query(a, a); upd(1, dfn[a]); if(a!=u) L=query(u, a); if(a!=v) R=query(v, a); upd(1, dfn[a], rec.mx1, rec.mx2); if(L.l==-1) res=R; else if(R.l==-1) res=L; else res=merge(L, R); if(w==res.mx1) del=min(del, w-res.mx2); else del=min(del, w-res.mx1); } cout<<res+del<<endl; return 0; }
这篇关于AcWing 356. 次小生成树的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2025-01-15你可能误会了!用 TypeScript 的正确姿势并不是这样子的
- 2025-01-14成本考量下,开源 CMS 内容管理系统为何脱颖而出
- 2025-01-14用Diffusers结合CivitAI模型、LoRAs和文本反转生成更高质量的图像
- 2025-01-14利用ChatGPT自动构建知识图谱的方法讲解
- 2025-01-14?? 缓存增强生成(CAG):一个崛起的RAG竞争对手?
- 2025-01-14Apache Spark及分布式计算概览
- 2025-01-14AWS入门第一篇——云基础与EC2实例详解
- 2025-01-14Apache Iceberg:现代数据栈中的“新一代Hadoop”?
- 2025-01-14深入理解 ECMAScript 2024 新特性:Promise.withResolvers
- 2025-01-13SRM vs SCM:企业管理中的差异战略与实践