我們已經熟知了求最小生成樹的方法,用kruskal,prim演算法都可以搞
那麼我們如何求次小生成樹呢?
這裡次小生成樹的定義是
邊權和嚴格大於最小生成樹的邊權和最小的生成樹次小生成樹嘛,肯定和最小生成樹脫不了關係
那麼我們首先求出最小生成樹
接下來,乙個比較顯然的思路是
列舉每一條未加入最小生成樹的邊,加入最小生成樹,同時在最小生成樹中刪除邊權最大的邊
如果你想到了這裡並寫出了**,那麼恭喜你
你在裡成功還有一步之遙成功掉進坑里了
比如下面的例子
藍邊表示最小生成樹中的邊,黃邊表示新加入的邊
在這種情況下,如果僅僅記錄最大值的話,得到的答案一定是錯的
所以我們還要記錄嚴格小於最大值的最大值
當產生衝突的時候我們需要刪除嚴格小於最大值的最大值
但是這樣效率太低了,每一次查詢都是\(o(n)\)的
有沒有更好的方法呢?
不要忘了,最小生成樹它是一棵樹呀
樹的鏈上最大最小值操作,你想到了什麼?
沒錯!樹上倍增
我們在倍增的過程中記錄下最大值和嚴格小於最大值的最大值
這樣每次查詢的複雜度就變成\(log(n)\)啦
整個演算法的流程大概是
求出最小生成樹
構造出倍增陣列
每次樹上倍增查詢
用kruskal是\(o(m\log m+q\log (n))\)
用prim是\(o(n\log n+q\log (n))\)
q為詢問次數
放一道裸題
// luogu-judger-enable-o2
#include#include#include#include#include#define int long long
using namespace std;
const int maxn=400001;
const int inf=1e15+10;
inline int read()
while(c>='0'&&c<='9')
return x*f;
}struct edge
e[maxn];
int enum=1;
void add(int x,int y,int z)
struct node
edge[maxn];
int head[maxn];
int num=1;
int n,m;
int fa[maxn],vis[maxn],sum;
int deep[maxn],f[maxn][21],maxx[maxn][21],minx[maxn][21];
void addedge(int x,int y,int z)
int find(int x)
int unionn(int x,int y)
int comp(const edge &a,const edge &b)
}}int lca(int x,int y)
int findmax(int x,int lca,int val)
}return ans;
}void work()
printf("%lld",ans);
}main()
kruskal();
deep[1]=1;
dfs(1,0);
pre();
work();
return 0;
}
最小生成樹 次小生成樹
一 最小生成樹 說到生成樹首先要解釋一下樹,樹是乙個聯通的無向無環圖,多棵樹的集合則被稱為森林。因此,樹具有許多性質 1.兩點之間的路徑是唯一的。2.邊數等於點數減一。3.連線任意兩點都會生成乙個環。對於乙個無向聯通圖g的子圖,如果它包含g的所有點,則它被稱為g的生成樹,而各邊權和最小的生成樹則被稱...
次小生成樹
演算法引入 設g v,e,w 是連通的無向圖,t是圖g的一棵最小生成樹 如果有另一棵樹t1,滿足不存在樹t t t1 則稱t1是圖g的次小生成樹 演算法思想 鄰集的概念 由t進行一次可行交換得到的新的生成樹所組成的集合,稱為樹t的鄰集,記為n t 設t是圖g的最小生成樹,如果t1滿足 t1 min,...
次小生成樹
分類 圖論 2013 02 12 15 03 32人閱讀收藏 舉報次小生成樹 在求最小生成樹時,用陣列path i j 來表示mst中i到j最大邊權。求完後,直接列舉所有不在mst中的邊,把它加入到mst中構成一棵新的樹,且該樹有環,此環是由剛加入的邊 i,j 造成的,所以可以通過刪除path i ...