點雙聯通分量,圓方樹和廣義圓方樹
阿新 • • 發佈:2018-11-10
點雙聯通分量
邊雙聯通分量想必看這篇部落格的同學就會,並且邊雙聯通分量理解和打起來比較簡單,就不再贅述了。
點雙聯通分量,類比邊雙的定義,它是原圖的極大無向子圖,滿足刪去子圖中任意一個節點以及與其相鄰的邊,其餘節點仍然連通。
如下圖,左中兩個均為一個點雙聯通分量,但最右邊圖中有兩個點雙聯通分量(上下兩部分),因為刪去中間的點後他們不能聯通
由最右邊的圖可以看出,一個點可能屬於多個點雙聯通分量,我們稱這些點為割點。
這也帶來了麻煩,我們處理邊雙的時候,直接彈棧即可,但此時我們是否需要多一些討論?
這就需要引入圓方樹
圓方樹
圓方樹本來是一種用來處理仙人掌問題的資料結構
廣義圓方樹實際上就是將其拓展到了一般的無向圖中,做起來差別不大,我們都可以統稱為圓方樹。
做題時,常常需要面對有關無向圖中兩點之間的簡單路徑(不經過重複點)的問題
無向圖中的路徑十分麻煩,圓方樹可以將我們所需要的資訊濃縮到一棵樹上。
我們將原本無向圖中的點稱為圓點。
對於一個點雙聯通分量,我們建立一個方點來代表它,點雙聯通分量中的所有點都向這個方點連邊。
我們將上面的這些圖建成圓方樹後,它長這樣:
可以看出,圓方樹中只存在圓點與方點之間的邊。
構造圓方樹,同樣可以利用tarjan演算法
有些模板是棧裡是儲存邊的(可以看出,一條邊只會出現在一個點雙中),但儲存點操作起來更加的方便
void tarjan(int k,int fa)
{
st[++st[0]]=k;
low[k]=dfn[k]=++dfn[0];
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa)
{
if(!dfn[p])
{
tarjan(p,k);
if(low[p]>=dfn[k])//棧中p所在的部分與k構成了一個點雙
//當low[p]>dfn[k]時,這個點雙只有一條邊兩個點。
{
lk(++n1,k),lk(k,n1);//新建方點n1,並向所有點雙中的點連邊
while(st[st[0]]!=p) lk(n1,st[st[0]]), lk(st[st[0]],n1),st[st[0]--]=0;
lk(n1,p),lk(p,n1),st[st[0]--]=0;
}
else low[k]=min(low[k],low[p]);
}
else low[k]=min(low[k],dfn[p]);
}
}
}
利用圓方樹,我們避免了大量的討論。
現在無向圖中的路徑問題變成了樹上路徑問題。
某些資料結構大神開發出了(仙人掌分治,虛仙人掌,仙人掌剖分等毒瘤玩意)。利用圓方樹,這些問題都轉化成樹上的問題,好寫又易於理解。