【學習】基環樹


基環樹,也是環套樹,簡單地講就是樹上在加一條邊。它形如一個環,環上每個點都有一棵子樹的形式。因此,對基環樹的處理大部分就是對樹處理和對環處理。顯然,難度在於后者。

扣環

這是幾乎所有基環樹處理的第一步。扣環的方法多種多樣,各有千秋,反正都是\(O(n)\)的。這里貼一下本人扣環的代碼。這個東西,稍微博采眾長一下就好了。

struct edge {
  int la,b,v;
} con[N << 1];
int tot, fir[N];
int vis[N], cnt, lop[N], dist[N], inl[N], rt;
int getlop(int pos,int fa) {
  if (vis[pos]) {
    rt = pos;
    return 1;
  }
  vis[pos] = 1;
  int tmp;
  for (int i = fir[pos] ; i ; i = con[i].la) {
    if (con[i].b == fa) continue;
    if (tmp = getlop(con[i].b,pos), tmp) {
      if (tmp == 1) {
		lop[++cnt] = pos;
		dist[cnt] = con[i].v;
		inl[pos] = 1;
		if (pos != rt) return 1;
      }
      return 2;
    }
  }
  return 0;
}

接下來就是題目了。


BZOJ1791 Island

題意:有一個基環樹和樹組成的森林,共有\(n\)個結點,邊有邊權。試求其中所有聯通塊的直徑之和。

\(n \leq 10^6\)

我們只用考慮如何求出一個基環樹的直徑。

首先,我們要對環上所有點的子樹求出它們的直徑和最大深度。然后,我們只用考慮在環上至少經過一條邊的路徑。那么,這種路徑在環上一定有起始點和終點。(假設路徑是從起始點開始,按順時針方向走達到終點)

不妨枚舉這段路徑在環上的終點。由於規定了這個點和方向,我們就可以拆環了。然后是一個經典的技巧,把環上元素復制一遍,就可以枚舉全部拆環方案。設環上有\(l\)個結點。那么,我們枚舉終點,就相當於在長度為\(2l\)的數組上不斷滑動一個長度為\(l\)的區間。

剩下的問題與基環樹已經沒什么關系了。設環上邊權的前綴和為\(sum\),環上結點的子樹的最大深度為\(dep\),那么,在環上起始點為\(i\),終點為\(j\)的路徑能得到的長度就是\(sum_j - sum_i + dep_j + dep_i\)。既然枚舉了\(j\),我們在滑動區間時就只用維護\(dep_i - sum_i\)的最大值就可以了。這個可以用單調隊列實現。

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


CF835F. Roads in the Kingdom

題意:有一棵\(n\)個結點的基環樹,邊有邊權。需要從環上刪去一條邊,以最小化直徑。輸出最小化后的直徑。

\(n \leq 2 \times 10^5\)

本題和上道題類似,做法也相近。我們只用枚舉刪掉的是哪一條邊,然后因為路徑長度是\(sum_j+dep_j-sum_i+dep_i\),我們用兩個set維護所有\(sum_i+dep_i\)\(-sum_i + dep_i\)。這樣就能求直徑只要從兩個set中分別取出最大值就好了。當然,還要特判兩個最大值是同一個結點的情況。

時間復雜度\(O(n \log n)\)


BZOJ2878 迷失游樂園

題意:有一棵\(n\)個結點的基環樹,邊有邊權。當你從一個結點出發后,你每次會等概率地選擇下一個未被訪問過的結點,並走到那個結點,直到與當前點相鄰的結點都被訪問過為止。試求等概率選擇出發點,所走的路徑長度的期望值。

\(n \leq 10^5\)

先考慮如何對樹做這個問題。我們設\(dp_i\)為從結點\(i\)開始,只向孩子結點走的路徑的期望長度。按照題意可以得到\(dp_i = \frac {\sum_{j \in ch_i} dis(i,j) + dp_j} {|ch_I|}\)。在計算出所有\(dp\)值后,再從父親向孩子結點更新,得到每個點的答案。這樣,就是兩遍樹形dp,一次從下往上更新,一次從上往下更新。

然后考慮對環的處理。環上的兩個結點互相到達,既可以按順時針方向走,也可以按逆時針方向走。因此,我們枚舉這個方向,就可以拆環了。然后,我們可以用類似的方法,求出環上結點之間的貢獻。這里特別提及一個細節,當在環上向右移動區間時,我們不但要刪去區間舊左端點的貢獻,還要考慮新左端點因為成為邊界,不能再向左走,其貢獻會增加。更確切地說,設舊左端點為\(u\),新左端點為\(v\),那么,原來對於\(v\)的,有\(\frac {1} {|ch_v| + 1}\)的可能向\(u\)走,但現在,就只有向它的子樹走的可能了,故\(v\)的貢獻會改變。

這樣,通過一次dfs,兩次環上處理,再加一遍dfs,就能解決本題。

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


上面的問題中,我們可以直接拆環,把環上元素復制一遍來解決。然而,對於另一些問題,我們需要使用枚舉環端點元素的狀態的技巧,以實現破環成鏈。


BZOJ1040 騎士

題意:有一個基環樹森林,含有\(n\)個結點。求其帶權最大獨立集。

\(n \leq 10^6\)

關於如何求樹的帶權最大獨立集,這里便不必贅述了。

考慮處理環,我們枚舉其中某個元素選還是不選,就能破環成鏈了。

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


ARC079F - Namori Grundy

題意:有一個弱聯通的有向圖,含有\(n\)個結點和\(n\)條邊。試問是否存在方案,賦給每個結點一個自然數權值\(val_i\),滿足對於所有結點\(u\)\(val_u = {\rm mex}\{val_v | (u,v) \in E\}\)。一個集合的\(\rm mex\)是沒有在這個集合中出現的最小自然數。

\(n \leq 2 \times 10^5\)

唯一能導致無解情況出現的就只有環了。我們扣環之后,對環上所有結點的外向樹做dfs,這樣就只用考慮環了。

順便一提,對於所有已知\(\{val_v | (u,v) \in E\}\)的結點\(u\)\(val_u\)直接暴力求就可以了。設\(a_i\)表示\(val_u = i\)時,以\(u\)為根的外向樹中最少需要的結點數(包括\(u\))。那么,可以得到:

  • \(a_0 = 1\)
  • \(a_i = \sum_{k=0}^{i-1} a_i + 1\)

對這個數列求通項,得到\(a_i = 2^i\)。因此暴力求\(\rm mex\)\(O(\log n)\)的。

\(S_u\)表示\(\{val_v | (u,v) \in E\}\),那么,對於環上所有結點\(u\)\(val_u\)要么是\({\rm mex} S_u\),要么是\(S_u\)中沒有出現的次小的自然數。后一種情況會出現當且僅當\(u\)在環上前一個結點的權值恰好等於\({\rm mex}S_u\)。於是,我們枚舉環上某一個結點是上面哪一種情況,在環上掃一遍判斷一下就好了。

時間復雜度\(O(n \log n)\)


小結:總算是對基環樹有一個初步的了解。上面例題雖然涉及的做法比較單一,但能反映出基環樹的問題大部分上就是樹和環,而環又通常轉化為鏈來處理。當然,也必然存在其他新穎的做法。而要豐富自己的認識,估計就只能靠見識更多的題目了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM