第一階段集訓(這篇先寫寫tarjan以及圓方樹)

2022-05-16 11:06:00 字數 3167 閱讀 2522

第一階段的集訓結束了w,不得不說oi太長時間不整是會退步的。

怎麼說好呢,集訓這幾天過的很充實,知識收貨很多,題調的也不少,自己的目標更明確了吧,不過這幾天集訓也是可以看出蒟蒻就是蒟蒻,還是太菜了。。。。不過會努力的w。

首先得感謝一下兔哥,假期還來給我們上課,還要飽受牙疼的折磨。。。再次感謝兔哥,%%%%%大神就是大神w。

結束之後想想這兩天都記住什麼了w,第一天講了tarjan,印象最深的還是圓方樹這個知識點吧,畢竟這個是個木有學習過的知識。

算了,要不我還是順道複習一下強聯通分量blabla什麼的吧。。。。。。。

+ 強聯通分量(不要告訴我不支援markdown)。。。。

在有向圖g中,如果兩個頂點u,v之間存在一條路徑u到v的路徑而且也存在一條v到u的路徑,則稱這兩個頂點u,v是強聯通的。如果有向圖g的每兩個頂點都強聯通,稱g是乙個強聯通圖。有向非強聯通圖的極大強聯通子圖,稱之為強聯通分量。若將有向圖中的強聯通分量都縮成乙個點,則原圖會形成乙個dag。

+ 那麼我們一般求強聯通分量的方法是tarjan,那麼它是怎麼實現的w?

其實,tarjan本質上是個dfs,dfs過程中向棧中push當前節點。當找到乙個強聯通分量的時候,(其包含的節點一定都在棧中連續排列),so我們把它們全部都pop掉,這些元素構成乙個強聯通分量。

每個節點u有兩個值——dfn[u]和low[u]。dfn[u]就是dfs序中的位置,low[u]就是u經過最多一條棧中橫叉邊所能到達的dfn最小的點。這裡我們解釋一下什麼是橫叉邊,因為我們tarjan是dfs,這棵dfs樹上有原圖上的邊,在dfs樹上由父親指向兒子的叫做樹枝邊,由父親指向兒子下面其他後代的叫做前向邊,由後代指向祖先的叫做後向邊,像其他的邊(乙個子樹中的點指向另乙個子樹中的點)稱之為橫叉邊。棧中橫叉邊,顧名思義,就是終點是棧裡面元素的邊。

那麼問題來了,如何求我們這個所謂的「經過最多一條棧中橫叉邊能到達的dfn最小的點的dfn」(也就是low)?;

當我們dfs到的當前點為點u,列舉u能到達的點v。若v沒被dfs過,遞迴dfs點v,並且用low[v]更新low[u](就是low[u] = min(low[u],low[v])),如果v被dfs過且仍在棧中,說明u—>v這條邊是一條棧中橫叉邊,用dfn[v]更新low[u]。

如果所有v列舉完以後,low[u]仍等於dfn[u],則此時棧中u及u以上的節點同屬於乙個強聯通分量,要一起彈出。

此處應該有。。。。。我也不知道它能不能看到,應該可吧

然後我們說一說tarjan的性質:

+ 各個強聯通分量出棧的順序是一定的——就是縮點形成的dag的拓撲序的逆序。

拓撲序就是dag中的一種排序,要求任一條邊的終點排在起點之後。

so,tarjan可以求拓撲序,好寫???

tarjan的**的話。

void tarjan(int u)

else if(ins[v])

}if(low[u] == dfn[u])while(stk[top--] != u);}}

例題講了挺多w,像聯通數(暴力bfs加02爆錘標程),以及迷失在公園裡找不到方向(逛公園)

我記得還講了無向圖的tarjan來著????

這個好像需要引入幾個概念???

+ 割點:將這個點刪去後圖不連通;

+ 割邊: 講這條邊刪去後圖不連通;

+ 點雙聯通分量:無割點的極大聯通子圖。

+ 邊雙聯通分量:無割邊的極大聯通子圖。

然後如何用tarjan求割邊,邊雙?

在無向圖的dfs樹中,原圖中的邊不可能是橫叉邊,只可能是前(後)向邊或樹枝邊。所以無向圖的tarjan相比有向圖很好寫,無需討論某條邊是否為棧中橫叉邊。

一條無向邊(u,v)是割邊(dfn[u] < dfn[v])當且僅當它是樹枝邊,而且low[v] > dfn[u](這時子樹v中所有點不能到達子樹之外)

這時我們發現,刪去原圖中所有割邊,剩下的連通塊都是邊雙。

然後還有tarjan求割點qww

當u不是dfs樹的根的時候,對於樹枝邊(u,v)(u為v的父親),如果low[v] >= dfn[u],就是子樹v中的點都不通往到子樹u之外,那麼刪去點u,子樹v中的點與子樹u以外的點不了聯通,u為割點。

但是呢,當u是dfs的根的時候,判斷low[v] >= dfn[u]是不夠的。因為對於任意的v都滿足這個條件啊w,(當子樹u以外沒有點,上面判斷割點的理由就不成立了w)。

so,我們怎麼判斷呢?如果u有至少兩個子樹,則u是割點,只有乙個子樹,u不是割點。

還有要值得注意的是,刪去所有割點,剩下的聯通塊並不是點雙qww。(這個還挺顯然的吧)。

那麼我們怎麼求點雙呢???

其中一種求點雙的方法是把邊push進棧中,當發現low[v] >= dfn[u]時,把邊(u,v)及它以上的所有邊從棧中彈出,這些邊所涉及的點共同形成乙個點雙(顯然包括u)。

另一種方法是像別的tarjan演算法一樣把點壓入棧中,當發現low[v]  >= dfn[u]時,把v及v以上的點從棧中彈出,再加上u,共同形成乙個點雙。**的話。。。。。

tarjan求割點:

void tarjan(int u,int pre)

}if(u == 1 && child_cnt == 1)

iscut[u] = 0;

}

求割邊,邊雙**:

void tarjan(int u,int pre)

}else if(v != pre)}}

下面終於進入了我們正題了,圓方樹!!!!!!!!!!!

what is it?

乙個無向聯通圖可以對應一棵圓方樹(廣義的)。

圓方樹中有圓點和方點。

圓點是原圖中存在的點,方點對應原圖的點雙。原圖中跨點雙的邊予以保留,點雙內部的邊刪掉,同時所有方點與其對應點雙中的圓點連邊。

此處應該有:

那麼我們怎麼建一棵圓方樹呢?

我們新建乙個鏈前,按照定義對每個點雙建乙個方點,然後把它和點雙中的所有點連邊,再把原圖中跨點雙的邊也連上。

顯然,可以一邊tarjan求點雙,一邊把邊連了。  

在這裡舉一道棵題:

給出一張圖,點有點權,每次詢問兩點之間的簡單路徑中,權值的最小值最小是多少。n,m,q <=1000000.

這個題我們把圓方樹建出,方點權值為包含圓點權值最小值,然後這個問題就轉化成了求鏈上最小值。可倍增,可樹剖。。。。over

集訓的第一階段結束

這一階段的集訓,就像老師所說讓我們了解一下網路安全主要包含什麼。今天我回顧了一下我從學長那裡學到了什麼 我學到了python語言的美麗和簡便 kail 虛擬機器的神奇 接觸到了網路傳輸的底色 php語言的綜合性。我接觸到了我以前從沒見過的軟體工具,從乙個盲目的小白變成了乙個有些許頭緒的小白。雖然我現...

第一階段練習

1 輸入乙個整數,把該整數分別按照八進位制 十進位制 十六進製制形式輸出 include stdio.h main 2 輸入乙個小數 整數部分3位 小數部分5位 把該小數分別按照以下格式輸出 小數部分4位寬度,整個數字8位寬度 小數部分3位寬度,整個數字9位寬度,空白部分使用0填充 include ...

第一階段 2015 12 2016 03

距離上一次寫部落格,大半年就過去了,這半年,都做了些什麼呢,生活又都有些什麼變化呢。2015的下半年是收穫的半年吧。第二,學ios的過程中結識了新的朋友,耳機哥,是很棒的一件事。第三,得到了乙份很美滿的愛情。最終在一起,還好沒錯過。第四,開始正視自己的不足,不再逃避,開始認真製作簡歷,並開始找工作,...