1. 程式人生 > >非嚴格/嚴格次小生成樹問題

非嚴格/嚴格次小生成樹問題

查詢 || 可能 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; }

非嚴格/嚴格次小生成樹問題