BZOJ 入门OJ P1634
Luogu P4180 BJWC 次小生成树
首先讲非严格次小生成树的做法。
先建立权值之和为\(W\)的最小生成树。接着枚举没有被并入到MST的边(u,v)(我们将把这条边加入到MST中,并在原MST中删去一条最大边,使新ST仍然联通),权值为\(W_e\)。查询树上u->v的路径,在路径选取一个边权最大值\(W_m\)。则当前枚举的答案为\(W-W_m+W_e\)。枚举所有的边之后,取最小值即可。
基本无需证明。这个代码就不给了,可以用倍增和树剖等实现。
接下来讲解严格次小生成树的做法。
原方法是枚举一条边\(W_e\),之后再寻找一条MST上的合法最大边(即在路径上)\(W_m\)。显然\(W_e \geq W_m\),因此可能由此得到的次小生成树并非严格。所以我们可以再查找路径上的严格次小值\(W_{m2}\),则显然\(W_e > W_{m2}\),所以由此得到的生成树一定严格小于MST。同样枚举所有的边,取最小值即可。
顺便一提,我这题错的地方忘记加了这个,跳点的倍增数组:
gup[i][j]=gup[gup[i][j-1]][j-1];
MMP
以及,两种算法的时间复杂度皆为:\(O(m\ logn)\)
补充:合并严格最大值和次大值
假设合并的两对最大值/次大值分别为\(m_1,s_1,m_2,s_2\),新的最大值/次大值为\(m,s\)。
我们可以证明,\(m\)一定会在\(m_1,m_2\)决出,则直接在两个里取个最大值就行。
然后\(s\)首先在\(s_1,s_2\)取最大值,若\(m_1\)不等于\(m_2\),再与\(min(m_1,m_2)\)进行比对。
// Code by ajcxsu
// Problem: Second Minium Spanning Tree
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10, M=3e6+10;
struct Edge {
int u,v;
ll w;
bool operator < (const Edge &b) { return w<b.w; }
} e[M];
bool S[M]; // 标记MST
int h[N],to[M],nexp[M];
ll W[M];
int p=1;
inline void ins(int a, int b, int w) {
nexp[p]=h[a], h[a]=p, to[p]=b, W[p]=w, p++;
}
int fa[N];
int Find(int x) {
if(!fa[x]) return x;
return fa[x]=Find(fa[x]);
}
bool Union(int a, int b) {
int af=Find(a), bf=Find(b);
if(af==bf) return false;
fa[af]=bf;
return true;
}
const int OP=20;
int dep[N], gup[N][OP], ma[N][OP], sma[N][OP];
void dfs(int x, int k) {
dep[x]=k;
for(int u=h[x];u;u=nexp[u])
if(!dep[to[u]]) {
gup[to[u]][0]=x;
ma[to[u]][0]=W[u];
sma[to[u]][0]=-1;
dfs(to[u], k+1);
}
}
ll m1,m2;
inline void upd(ll x) {
if(x>m1) m2=m1, m1=x; // m1的值一定要移到m2
else if(x<m1 && x>m2) m2=x;
}
void lca(int a,int b) {
m1=m2=0;
if(dep[a]<dep[b]) swap(a,b);
for(int j=OP-1;j>=0;j--)
if(dep[gup[a][j]]>=dep[b]) upd(ma[a][j]), upd(sma[a][j]), a=gup[a][j];
if(a==b) return;
for(int j=OP-1;j>=0;j--)
if(gup[a][j]!=gup[b][j]) {
upd(ma[a][j]), upd(sma[a][j]);
upd(ma[b][j]), upd(sma[b][j]);
a=gup[a][j], b=gup[b][j];
}
int j=0;
upd(ma[a][j]), upd(sma[a][j]);
upd(ma[b][j]), upd(sma[b][j]);
}
template <typename T> void gn(T &x) {
x=0;
char ch=getchar();
while(ch<‘0‘ || ch>‘9‘) ch=getchar();
while(ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘, ch=getchar();
}
int main() {
int n,m;
gn(n), gn(m);
for(int i=0;i<m;i++)
gn(e[i].u), gn(e[i].v), gn(e[i].w);
sort(e,e+m);
ll mst=0;
for(int i=0, cnt=0;i<m && cnt!=n-1;i++) {
if(Union(e[i].u, e[i].v)) {
S[i]=1, mst+=e[i].w;
ins(e[i].u, e[i].v, e[i].w), ins(e[i].v, e[i].u, e[i].w);
cnt++;
}
}
dfs(1,1);
int a[4],ee,ff;
for(int j=1;j<OP;j++)
for(int i=1;i<=n;i++) {
gup[i][j]=gup[gup[i][j-1]][j-1];
a[0]=ma[i][j-1], a[1]=ma[gup[i][j-1]][j-1];
a[2]=sma[i][j-1], a[3]=sma[gup[i][j-1]][j-1];
ee=max(a[0],a[1]), ff=max(a[2],a[3]);
if(a[0]!=a[1]) ff=max(ff, min(a[0],a[1]));
ma[i][j]=ee, sma[i][j]=ff;
}
ll ans=0x7ffffffffffffll;
for(int i=0;i<m;i++)
if(!S[i]) {
lca(e[i].u, e[i].v);
if(m1!=e[i].w) ans=min(ans, -m1+e[i].w);
else ans=min(ans, -m2+e[i].w);
}
printf("%lld\n",mst+ans);
return 0;
}
原文:https://www.cnblogs.com/acxblog/p/8947292.html