線段樹分治學習筆記

2022-09-19 17:39:08 字數 3340 閱讀 4965

也許更好的閱讀體驗。

模板題p5757:一張 \(n\) 個點的圖,有 \(m\) 條無向邊會在 \([l_i,r_i]\) 時刻存在,求在 \([1,k]\) 時刻中每個時刻整張圖是否是一張二分圖。

\(1\leq n\leq 10^5\),\(1\leq l_i\leq r_i\leq k\leq 10^5\),\(1\leq m\leq 2\times 10^5\)。

前置知識:擴充套件域並查集判定二分圖,可撤銷並查集,線段樹。

考慮二分圖中一條邊的意義為其兩邊端點不在同一部,則用 \(x,x+n\) 代表 \(x\) 與 \(x\) 的對立點,和 \(x\) 的對立點處於同一集合的則和 \(x\) 不在同一部,那麼二分圖中一條邊就是互相和對方的對立點相連,若出現 \(x\) 和 \(x+n\) 處於同乙個集合則說明不是二分圖,並且若 \(x,y\) 是圖變為非二分圖加的最後一條邊,那麼 \(x,x+n\),\(y,y+n\) 之間會分別在同乙個集合中。

可撤銷並查集的原理是考慮並查集常見的兩種優化,路徑壓縮和按秩合併,這裡的秩可以是 size,depth,甚至是 rand,單獨用 size 作為秩進行按秩合併的好處是,把 \(x\) 並到 \(y\) 上的操作只有累加 \(sz_x\) 至 \(sz_y\),更新 \(fa_x\) 為 \(y\),不難發現,若最近一次操作就是把 \(x\) 並到 \(y\),那麼這個並上去的操作可以把 \(sz\) 減回去,\(fa\) 恢復,則做到了撤銷。其限制在於只能按照並的順序倒著撤銷,即按照棧的順序,否則後來並到 \(x\) 上的貢獻不能僅減去 \(sz_x\) 來消除。

於是總複雜度 \(o(m\log k\log n)\),乙份參考**實現。

例題cf576e:一張 \(n\) 個點 \(m\) 條邊的無向圖,顏色從 \(1\) 到 \(k\) 編號,初始邊上顏色均為 \(0\),\(q\) 次修改操作把第 \(e_i\) 條邊的顏色修改為 \(c_i\),定義乙個合法狀態為 \(1\cdots k\) 每個顏色的邊集分別構成的子圖都是二分圖,若修改之後由合法狀態變為非法狀態,則此次修改無效,求每次修改是否有效。

\(1\leq n,m,q\leq 5\times 10^5\),\(1\leq k\leq 50\)。

考慮這題的形式和模板題非常像,資料範圍也支援我們對於 \(k\) 種顏色分別維護,所以考慮如何進行轉化使得可以讓我們類似地解決這個問題。

現在的困難在於,我們沒有上一題一樣得到一條邊在某個時間內屬於某個顏色的資訊,於是我們考慮每條邊的顏色情況。若邊 \(e_i\) 相鄰的兩次顏色修改時刻為 \(x,則我們可以看作,它在 \([x,x]\) 時刻修改了顏色,並且由於是單點變更,我們可以到葉子節點的時候嘗試修改並判斷是否合法,以此我們就能得到其在 \((x,y)\) 時間中的顏色,就和上面一題完全一致了。這裡的邏輯是其遞迴到底層之後是沒有被前面或後面的修改所包含的,而且由於是先處理左兒子,到 \((x,y)\) 的時候一定已經經過了葉子 \(x\),可以知道其對應的顏色並加入,而且由於是在時間範圍內加入,也並沒有刪除的操作。

於是總複雜度 \(o(q\log q\log n+nk)\),乙份參考**實現。

例題cf603e:乙個 \(n\) 個點的無向圖,\(m\) 次加入一條帶權邊 \((u,v,w)\),求每次加入後是否能選出乙個邊集使得點 \(\operatorname_\) 均為奇數,如果可以,最小化該邊集的最大權值並輸出。

\(1\leq n\leq 10^5\),\(1\leq m\leq 3\times 10^5\)。

這道題首先需要分析一下性質,首先選出來的邊集中若存在環,我們顯然可以把環刪掉以得到不劣的答案,所以構成的每個聯通塊都是樹。考慮一棵什麼樣的樹可以滿足性質,不難發現除了根有奇數個兒子,其它點都只有偶數個兒子,這啟發我們通過對一棵任意樹進行調整來控制根和非根的葉子數量:若乙個點有偶數個兒子,則保留它與父親的邊,否則刪掉,使其成為子樹的根節點。如果能完整經過這樣的調整,新形成的若干棵樹就應該是合法的。考慮從葉子節點來看,乙個非根節點的子樹為其自身加上偶數個兒子的子樹,則其 size 是奇數;乙個根節點是它本身加上奇數個 size 為奇數的兒子的子樹,所以它的 size 是偶數;從另乙個角度,每個點度數都是奇數,而總度數是偶數(每條邊貢獻 \(2\)),也要求其 size 是偶數。則一棵 size 是偶數的樹,若其每個非根節點 size 都為奇數,則一定合法,否則割掉所有 size 的偶數的子樹就合法了,於是聯通塊 size 是偶數即為每個點度數為奇數的充要條件。

那麼可以得到,若乙個聯通塊點數為偶數,我們就可以通過一些調整,刪除一些聯通塊中的邊使其滿足每個點的度數都是奇數,至此便得到了這個問題靜態版解決方法:按邊權從小到達加入並查集,直到不存在 size 為奇數的聯通塊。

但是對於動態的問題,我們如果沿用上面的做法,那麼不按邊權順序加邊的後果是,可能一次修改會導致整張圖的結構發生巨大變化,這不是用目前的簡單做法可以維護的。所以我們需要挖掘性質,找到其它做法。首先考慮這樣一件事情,一條邊可能存在於邊集中的時間是乙個區間或不存在,由於答案是單調不公升的,觀察一下可以得到,第 \(i\) 條邊可能產生貢獻的時間為 \([i,j]\),其中 \(j\) 是最後乙個滿足 \(ans_j\ge val_i\) 的時刻。我們希望能得到這個區間,而答案又是單調不公升的,所以我們把邊按邊權從小到大排序,並維護乙個從前往後不退的指標,線段樹分治到葉子節點 \([l,l]\) 之後,如果還沒有滿足不存在奇數大小聯通塊,則暴力推進指標並加入編號 \(的邊直到合法。那麼 \([i,j]\) 的 \(j\) 怎麼求呢?發現我們每次推進指標並加入邊 \(i\),其對應的 \(j\) 就是 \(l\),那麼由於我們又是從後往前遍歷的,則此時把 \(i\) 放到 \([i,l)\) 上就好了,即這是乙個一邊分治一邊覆蓋的過程,覆蓋的位置是根據分治的過程來決定的。

總複雜度依然是兩隻 \(\log\),乙份參考**實現。

同樣是兩隻 \(\log\) 的複雜度,這題還可以用整體二分來解決,下面介紹一下整體二分的做法。

因為答案是單調的,我們用函式 \(\text(l,r,l,r)\) 表示 \(ans_\in [l,r]\),並且 \(i且 \(val的邊已經加入可撤銷並查集,接下來考慮如何分治。

和普通整體二分一樣地,我們計算出 \(m=\lfloor \frac2 \rfloor\),\(ans_\) 的值 \(p\),並遞迴處理 \(\text(l,m-1,p,r),\text(m+1,r,l,p)\),那麼現在的問題就在於如何求 \(ans_\)。

現在已經加入的邊滿足 \(i且 \(val,那麼還需要加入的邊有:

\(l\leq i\leq m\) 且 \(val< l\) 的邊,對答案無影響,直接加入;

\(l\leq val\leq r\) 且 \(i\leq m\) 的邊,按 \(val\) 從小到大加入,直到滿足條件,則此時的邊權就是答案;

於是就可以得到 \(p\),再在遞迴之前恢復/加邊到滿足遞迴前置要求(\(i且 \(val的邊已經加入)再遞迴處理即可。

總複雜度兩隻 \(\log\),實測跑得比線段樹分治略快,乙份參考**實現。

線段樹分治學習筆記

參考部落格1 參考部落格2 核心思想 適用範圍 遇到如下問題 例題 p5787 二分圖 模板 線段樹分治 對時間軸建立線段樹。維護區間內存在的邊集,用 vec to rvector vector 進行維護。查詢時從根節點出發開始遍歷,遞迴處理時將當前節點存在的邊進行合併,判斷是否為二分圖。若到達某個...

線段樹分治學習筆記

模板 洛谷p5787 用按秩合併的帶權並查集維護連通塊中點之間邊數的奇偶性來判斷是否存在負環 用棧儲存撤銷操作,複雜度 o nlog 2 include define il inline define ls k 1 define rs k 1 1 define pb push back define...

樹 點分治學習筆記

不做筆記的後果是我完全忘記了我在5個月前就學過點分治 去洛谷做題才發現的 點分治大概是用於樹上路徑的求解。點分治分4步走 1,對當前子樹找重心 固定 void getroot int u,int fa ms u max ms u tsiz siz u if ms u u 2,把樹上距離 邊權 存進臨...