非嚴格/嚴格次小生成樹問題
阿新 • • 發佈:2018-04-26
查詢 || 可能 second namespace 復雜度 typename turn cpp
轉載自新博客:https://acxblog.site/archives/smst-problem.html
Problem
BZOJ 入門OJ P1634
Luogu P4180 BJWC 次小生成樹
Introduction
首先講非嚴格次小生成樹的做法。
先建立權值之和為\(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
// 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;
}
非嚴格/嚴格次小生成樹問題