一行小字。這篇博客是個人gg以前寫的博客,一直想寫博客,可是直到gg,都沒寫幾篇博客。我再把這篇博客翻出來的時候,不知已通過了半年仍是一年了。博客園的博客系統作的不錯,就是數學公式和一些圖片會亂,圖片的話手動上傳一下還ok。數學公式不影響閱讀,你們自行理解就ok嚶嚶~
最小生成樹Kurskal算法c++
最近公共祖先LCA!算法
一個有 n 個結點的連通圖的生成樹是原圖的極小連通子圖,且包含原圖中的全部 n 個結點,而且有保持圖連通的次少的邊。數組
洛谷P4180 [BJWC2010]嚴格次小生成樹markdown
把全部生成樹按照權值之和從小到小排序,求排在第二位的生成樹。注意,若是最小生成樹不惟一,次小生成樹的權值和最小生成樹相同。函數
次小生成樹不會和最小生成樹相同,所以能夠枚舉最小生成樹中不在次小生成樹中出現的邊。 注意最小生成樹只有n-1條邊,因此只需枚舉n-1次。每次在剩下的邊裏,求一次最小生成樹。學習
Kruskal算法求出最小生成樹優化
枚舉最小生成樹的每一條邊,對這條邊作標記,再進行一次Kruskal算法,Kruskal算法中,跳過被標記的邊,求最小生成樹,記錄答案。spa
去步驟2中所記錄答案的最小值即爲次小生成樹的邊權之和。(可直接在步驟2中進行)調試
$O(M\log M+M)$code
$O(NM)$
$O(N)$
總複雜度$O(NM)$
有沒有更快些的解決方法呢?
- T爲圖G的一棵生成樹,對於非樹邊a和樹邊b,插入邊a而且刪除邊b的操做記爲(+a,-b)
- 若是T+a-b仍然是一棵生成樹,稱(+a,-b)是一個可行交換
- 由T進行一次可行交換之後獲得的新的生成樹的集合稱爲T的臨集
顯然,可得定理:次小生成樹在最小生成樹的臨集中
- 枚舉要加入哪條新邊,在最小生成樹中加入一條邊u-v之後,圖上會出現一條迴路,若是想保持該圖爲生成樹,需刪除一條邊,刪除的邊必須是在最小生成樹上u到v的路徑上,若是想要刪除邊後的新的生成樹有可能爲次小生成樹,必需要求刪除的邊這條路徑的最長邊。
如圖,枚舉出一條不在最小生成樹上的邊E1,若是鏈接E1,E1兩端點爲(1,4),若是想鏈接E1後依然保證新圖爲生成樹,需刪除路徑(1,4)上一條最小生成樹上的邊。爲了求出次小生成樹,咱們選擇刪除原樹上的儘量大的邊,即刪除邊(3,4),鏈接邊E1。
如圖爲新生成的樹。
總結一下,新的算法是這樣的
那麼枚舉每一條邊複雜度是$O(M)$,對每一條邊用dfs求(u,v)上的最長&次長邊,複雜度是$O(N)$,總複雜度是$O(MN)$沒有遍低呀~
所以,我沒須要對算法進行優化,能夠經過dfs$O(N)$來預處理每一個點到其k代父節點的路徑上的最長邊和次長邊,再經過lca倍增的方法在$O(\log N)$複雜度下求出u,v兩點之間的最長邊和次長邊。
對於每個點u,咱們記記錄anc[u][k]數組,u是節點編號,k表明其$2^k $代父節點。
記錄m1[u][k]數組,其中,u是節點編號,k表明從u節點往上$2k$個點,數組的值是u點到u節點上方$2k $個點間的最長邊,可得狀態轉移方程
$$m1[u][k]=\max(m1[u][k-1],m1[anc[u][k-1]][k-1])$$
記錄m2[u][k]數組,u,k含義同m1,數組的值是u點到u節點上方$2^k $個點間的次長邊,可得狀態轉移方程
$$m2[u][k]=\begin{cases} \max(m2[u][k-1],m2[anc[u][k-1][k-1]), & \text{if }m1[u][k-1]\ne m1[anc[u][k-1]][k-1] \ \min(m1[u][k-1],m1[anc[u][k-1][k-1]), & \text{if }m1[u][k-1]= m1[anc[u][k-1]][k-1] \end{cases}$$
通過一次dfs後,咱們便可預處理完成以上內容。
遍歷$N$個點,每一個點要進行$\log N$次計算,複雜度爲 $O(N\log N)$
對於點u和v,咱們在有預處理的狀況下想求其兩點之間路徑的最長邊和次長邊,可進行如下操做
用倍增法求LCA的方式將兩個點向上跳,跳的同時記錄m1和m2的值。
向上跳的作法與倍增法求LCA相同,若是沒有了解,請先學習倍增法求LCA,此處不作過多介紹。
具體代碼以下:
/*參數ma爲要新建的邊的權值,由於要求嚴格次小生成樹, 因此在原樹中刪除的邊必須嚴格小於新建的邊的權值, 即lca函數的返回值(也就是在原樹中刪除的邊的權值)需嚴格小於參數ma。*/ long long findBig(long long u,long long i,long long ma){ if(m1[u][i]!=ma){ return m1[u][i]; } return m2[u][i]; } long long lca(long long u,long long v,long long ma){ if(dep[u]<dep[v]){ swap(u,v); } long long res = -INF; for(int i = 20;i>=0;--i){ if(dep[ancestor[u][i]]<dep[v]){ continue; } res = max(res,findBig(u,i,ma)); u = ancestor[u][i]; } if(u==v){ return res; } for(int i = 20;i>=0;--i){ if(ancestor[u][i]!=ancestor[v][i]){ res = max(res,findBig(u,i,ma)); res = max(res,findBig(v,i,ma)); u = ancestor[u][i]; v = ancestor[v][i]; } } res = max(res,findBig(u,0,ma)); res = max(res,findBig(v,0,ma)); return res; }
總複雜度爲:$O(MlogN)$
// // Created by SeverusNg on 2020/7/24. // #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> const long long MAXN = 100005; const long long MAXM = 300005; const long long INF = 0x3f3f3f3ff3f3f3f; using namespace std; struct edge{ long long u,v,w; int next; bool operator<(const edge &e)const{ return w<e.w; } } ed[MAXM*2],pool[MAXM]; int head[MAXN]; int topE; void addE(int u,int v,int w){ ed[++topE].u = u; ed[topE].v = v; ed[topE].w = w; ed[topE].next = head[u]; head[u] = topE; return; } int fa[MAXN]; int find(int u){ if(fa[u]==u){ return fa[u]; } fa[u] = find(fa[u]); return fa[u]; } void merge(int u,int v){ u = find(u); v = find(v); fa[u] = v; return; } bool use[MAXM]; long long N,M; long long mst; void kruskal(){ sort(pool+1,pool+M+1); for(int i = 1;i<=M;++i){ long long u = pool[i].u; long long v = pool[i].v; long long w = pool[i].w; if(find(u)!=find(v)){ mst += w; merge(u,v); addE(u,v,w); addE(v,u,w); use[i]=true; } } return; } int dep[MAXN]; int ancestor[MAXN][25]; long long m1[MAXN][25]; long long m2[MAXN][25]; long long findBig(long long u,long long i,long long ma){ if(m1[u][i]!=ma){ return m1[u][i]; } return m2[u][i]; } void dfs(long long u,long long father,long long w){ ancestor[u][0] = father; dep[u] = dep[father]+1; m1[u][0]=w; m2[u][0]=-INF; for(int i = 1;i<=20;++i){ ancestor[u][i]=ancestor[ancestor[u][i-1]][i-1]; m1[u][i]=max(m1[u][i-1],m1[ancestor[u][i-1]][i-1]); m2[u][i]=max(m2[u][i-1],m2[ancestor[u][i-1]][i-1]); if(m1[u][i-1]!=m1[ancestor[u][i-1]][i-1]){ m2[u][i]=max(m2[u][i],min(m1[u][i-1],m1[ancestor[u][i-1]][i-1])); } } for(int i = head[u];i!=0;i=ed[i].next){ long long u,v,w; u = ed[i].u; v = ed[i].v; w = ed[i].w; if(v==father){ continue; } dfs(v,u,w); } return; } long long lca(long long u,long long v,long long ma){ if(dep[u]<dep[v]){ swap(u,v); } long long res = -INF; for(int i = 20;i>=0;--i){ if(dep[ancestor[u][i]]<dep[v]){ continue; } res = max(res,findBig(u,i,ma)); u = ancestor[u][i]; } if(u==v){ return res; } for(int i = 20;i>=0;--i){ if(ancestor[u][i]!=ancestor[v][i]){ res = max(res,findBig(u,i,ma)); res = max(res,findBig(v,i,ma)); u = ancestor[u][i]; v = ancestor[v][i]; } } res = max(res,findBig(u,0,ma)); res = max(res,findBig(v,0,ma)); return res; } int main(){ scanf("%lld%lld",&N,&M); for(int i = 1;i<=N;++i){ fa[i] = i; } for(int i = 1;i<=M;++i){ scanf("%lld%lld%lld",&pool[i].u,&pool[i].v,&pool[i].w); } kruskal(); dfs(1,0,-INF); long long ans = INF; for(int i = 1;i<=M;++i){ if(use[i]){ continue; } long long u = pool[i].u; long long v = pool[i].v; long long w = pool[i].w; ans = min(ans,mst+w-lca(u,v,w)); } printf("%lld\n",ans); return 0; }
嚶嚶嚶,完結撒花~
啊,從建博客,各類調試,到寫完這一篇文章,再加上摸魚,擡頭一看,天已經亮了,搞了一個通宵~
不過看到成果,心裏仍是十分高興的~
若有錯誤,但願巨佬您可以在評論區指正,感激涕零~