日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

连通性相关

發布時間:2023/12/3 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 连通性相关 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

強聯通分量

強連通:有向圖 \(G\) 強連通表示,\(G\) 中任意兩個結點連通。

強連通分量( Strongly Connected Components ,簡稱 \(\operatorname{SCC}\) ):極大的 強連通子圖。

Tarjan

維護了以下兩個變量:

  • \(dfn\) :深度優先搜索遍歷時結點 \(u\) 被搜索的次序 。

  • \(low\) :設以 \(u\) 為根的子樹為 \(subtree(u)\)\(low\) 定義為以下結點的 \(dfn\) 的最小值: \(subtree(u)\) 中的結點;從 \(subtree(u)\) 通過 一條 不在搜索樹上的邊能到達的結點 。

從根開始的一條路徑上的 \(dfn\) 嚴格遞增,\(low\) 嚴格非降。

對于一個連通分量圖,我們很容易想到,在該連通圖中有且僅有一個 \(dfn[u]=low[u]\) 。該結點一定是在深度遍歷的過程中,該連通分量中第一個被訪問過的結點,因為它的 \(dfn\) 值和 \(low\) 值最小,不會被該連通分量中的其他結點所影響。

因此,在回溯的過程中,判定 \(dfn[u]=low[u]\) 的條件是否成立,如果成立,則棧中從 后面的結點構成一個 \(\operatorname{SCC}\)

P2341 [HAOI2006]受歡迎的牛 G \(-\) 模板

$\texttt{code}$ #define Maxn 10005 #define Maxm 50005 void tarjan(int u) {dfn[u]=low[u]=++Time; s.push(u),ins[u]=true;for(int i=hea[u];i;i=nex[i]){if(!dfn[ver[i]]) tarjan(ver[i]),low[u]=min(low[ver[i]],low[u]);else if(ins[ver[i]]) low[u]=min(dfn[ver[i]],low[u]);}if(dfn[u]==low[u]){sum+=1;do{belong[u]=sum;u=s.top(); s.pop(); ins[u]=false;cnt[sum]+=1;} while(dfn[u]!=low[u]);} }for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);

時間復雜度 \(O(n+m)\)

Kosaraju

復雜度 \(O(n+m)\)

Garbow

復雜度 \(O(n+m)\)

我們可以利用強聯通分量將一張圖的每個強連通分量都縮成一個點。

然后這張圖會變成一個 \(\operatorname{DAG}\),可以進行拓撲排序以及更多其他操作 。

應用 \(-\) 縮點

P3387 【模板】縮點

for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=tot[0];i++)if(belong[fro[0][i]]!=belong[ver[0][i]])add(1,belong[fro[0][i]],belong[ver[0][i]]),ind[belong[ver[0][i]]]++; topo();

割點與橋

在無向圖中刪去這個點 \(/\) 邊會使極大強聯通增大,那么這個點 \(/\) 邊為割點 \(/\) 橋 。

注意這里的 \(dfn\) 表示不經過父親,能到達的最小的 \(dfn\)

割點

P3388 【模板】割點(割頂)

關鍵條件:

  • \(u\) 是根節點,當至少存在 \(2\) 條邊滿足 \(low[v] >= dfn[u]\)\(u\) 是割點 。

  • \(u\) 不是根節點,當至少存在 \(1\) 條邊滿足 \(low[v] >= dfn[u]\)\(u\) 是割點 。

$\texttt{code}$ void tarjan(int u,int fa) {dfn[u]=low[u]=++Time;for(int i=hea[u];i;i=nex[i]){if(!dfn[ver[i]]){tarjan(ver[i],u),low[u]=min(low[ver[i]],low[u]);if(low[ver[i]]>=dfn[u]) cnt[u]+=1;}else if(ver[i]!=fa) low[u]=min(dfn[ver[i]],low[u]);} }for(int i=1;i<=n;i++) if(!dfn[i]) cnt[i]-=1,tarjan(i,0); for(int i=1;i<=n;i++) if(cnt[i]>=1) ans+=1;

割邊(橋)

關鍵條件:

  • 當存在一條邊條邊滿足 \(low[v] > dfn[u]\) 則邊 \(i\) 是割邊

關鍵部分的代碼:

注意:記錄上一個訪問的邊時要記錄邊的編號,不能記錄上一個過來的節點(因為會有重邊)!!!

$\texttt{code}$ void tarjan(int x,int Last_edg) {dfn[x]=low[x]=++Time;for(int i=hea[x];i;i=nex[i]){if(!dfn[ver[i]]){tarjan(ver[i],i);low[x]=min(low[x],low[ver[i]]);if(low[ver[i]]>dfn[x]) edg[i]=edg[i^1]=1;}else if(i!=(Last_edg^1)) low[x]=min(low[x],dfn[ver[i]]);} }for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,0); for(int j=2;j<=tot;j+=2) ans+=tag[j];

雙聯通分量

邊雙聯通分量

顯然,找出每一個橋,去掉這些橋之后的每一個聯通塊都是一個邊雙聯通字圖。

注意:用邊雙縮點的時候先處理出割邊,之后用 \(\text{dfs}\) 求出每一個雙聯通分量,不用棧!!

例題:P2860 [USACO06JAN]Redundant Paths G

$\texttt{solution}$

一句話題意:要將原圖轉化為邊雙聯通圖需要添加的最少邊數

我們可以先將所有的橋找出來,并同時對所有邊雙縮點,會得到一顆縮完點的、由橋構成的“樹”。

我們發現這棵“樹”上“葉子結點”的個數除二向上取整就是需要添加的邊的條數。

點雙連通分量

小粉兔的圓方樹——點雙詳解

【模板】點雙連通分量

回憶 \(low\) 的定義,就是 \(x\) 的子樹內最多經過一條反祖邊或一條向父親的邊能夠到達的最小的 \(dfn\) 值。

\(x\) 不是這個連通塊的根時,如果存在一條邊滿足 \(low(ver)\ge dfn(x)\),那么 \(x\) 就是一個個點。

\(x\) 是根時,\(x\) 需要存在至少兩條邊滿足以上條件。

這是因為當 \(x\) 為根時,沒有父親與之相連。

那么當我們在求點雙時,只需要判斷子樹內的 \(low\) 是否會 \(<dfn(x)\),若會,則說明子樹中的點能夠到達 \(x\) 的祖先,\(x\) 必然不是個點。而若不會,即 \(low(ver)\ge dfn(x)\),則說明 \(x\) 是這個點雙的頂端個點,這時可以將遞歸進入的點都彈出,直至棧頂元素變為 \(ver\),再將 \(ver\) 從棧中彈出,加上 \(x\) 就是這個點雙聯通分量了。

還要注意孤立點的情況。

如果需要處理點雙中點的個數,那么可以在棧中存放點,例如一下代碼:

void tarjan(int x) {dfn[x]=low[x]=++Time,sta[++tp]=x;if(tp==1 && !hea[x]) { SCC[++sum].pb(x); return; }for(int i=hea[x];i;i=nex[i]){if(!dfn[ver[i]]){tarjan(ver[i]);low[x]=min(low[x],low[ver[i]]);if(low[ver[i]]>=dfn[x]){sum++;do { SCC[sum].pb(sta[tp--]); }while(sta[tp+1]!=ver[i]);SCC[sum].pb(x);}}else low[x]=min(low[x],dfn[ver[i]]);} }

而如果需要處理點雙中的邊,那么就需要在棧中存邊,如以下代碼:

void tarjan(int x,int fa) {dfn[x]=low[x]=++Time;int cntson=0;for(int i=hea[x];i;i=nex[i]){if(!dfn[ver[i]]){sta[++tp]=i,tarjan(ver[i],i),cntson++;low[x]=min(low[x],low[ver[i]]);if(low[ver[i]]>=dfn[x]){isdian[x]=true,sum++;int tmp=0;do{tmp=sta[tp--];scc[sum].pb(fro[tmp]);scc[sum].pb(ver[tmp]);}while(tmp!=i);}}else if(i!=(fa^1)) low[x]=min(low[x],dfn[ver[i]]); }if(fa==0 && cntson==1) isdian[x]=false; }

總結

以上是生活随笔為你收集整理的连通性相关的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。