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;
}