線段樹分治

2021-09-12 15:44:23 字數 3131 閱讀 8416

動態圖聯通性(可離線)

(loj121)給你一張無向圖,你要支援如下操作:

1:刪除一條邊

2:加入一條邊

3:查詢某兩個點對間是否聯通

②離線做法:線段樹分治

口胡做法:

把操作的順序當做時間。

每條邊維護乙個存活區間,代表這條邊在這個時間區間裡面活著。

對時間軸建立一顆線段樹,從線段樹根開始dfs。

進入乙個子樹就把它這個子樹中完全存活的邊的兩個點用按秩合併並查集合並。

一直到乙個葉子的時候統計答案。

退出乙個子樹,或者到了乙個葉子回去的時候。把這個子樹內新加的這些存活的邊刪去。

(因為一條邊只會出現一次,所以可以用乙個陣列來存之前出現過的邊的標號)

注意點:

①:並查集合並之前要注意f1!

=f

2f1!=f2

f1!=f2

,否則會有很多冗餘操作,會ttt。

②:注意這東西是無向邊。所以一開始讀入的時候給邊定個序就可以了。否則會刪不了查不了。

③:如果一條邊重複多次加入刪除,可以在每次刪除的時候把陣列那一位清空就好了。

維護動態圖的其它性質

bzoj4025 給乙個動態圖,問每一時刻是否是二分圖。允許離線。

線段樹分治就套乙個板子,關鍵是怎麼判斷二分圖。

我們知道,乙個圖是二分圖當且僅當它沒有奇環。考慮用並查集維護乙個點到真正圖的根的距離。原因是我們getfa是在bcj的樹上進行的,但是要建立起這個樹和真正圖的乙個對應。考慮這樣做那些重複計算的邊恰好被異或掉了。畫個圖好些

如果一開始兩邊是聯通的,那麼只要判斷是否會造成奇環就好了。用get

di

sgetdis

getdis

一路異或上去就得到了兩個點之間的真實距離的奇偶性。判斷一下就好了。

如果一開始兩邊是不聯通的,那麼只有乙個點的dis

disdi

s的奇偶性會改變,就是小堆的代表元。考慮其它路徑上的點在異或的時候會全部抵消完。就沒有了影響,所以只要修改小堆代表元的那個dis

disdi

s就行了。

這裡有乙個小優化,如果乙個點為根的時候已經存在奇環了。那麼它的子樹裡的點也一定存在了。所以不用遞迴下去了。

有乙個小問題,因為這裡點數是1e5

1e51e

5級別的,就不能用陣列來存乙個邊是否存在。可以使用uno

rder

ed_m

ap

unordered\_map

unorde

red_

map等資料結構維護。

實現起來有幾個小細節:

①:資料是有可能st=

ed

st=ed

st=e

d的,這個時候是沒有存活區間的,需要特判。不然re

②:寫ifif

if的時候要注意括號括起來。。。

#include

#include

#include

#include

#define ls (k<<1)

#define rs ((k<<1)|1)

using

namespace std;

const

int n =

1e5+

5, m =

2e5+5;

struct edge a[m]

;struct node

node

(int x_,

int y_)

}sta[m]

;int fa[n]

, dis[n]

, ans[n]

, sta1[m]

, siz[n]

;int n, m, cnt, t, top, top1, flag;

inline

intread()

while

(ch >=

'0'&& ch <=

'9')

return x * f;

}vector<

int> tr[n<<2]

;inline

void

update

(int k,

int s,

int t,

int l,

int r,

int c)

int mid =

(l + r)/2

;if(t <= mid)

update

(ls, s, t, l, mid, c)

;else

if(s > mid)

update

(rs, s, t, mid +

1, r, c)

;else

}inline

intgetf

(int x)

inline

intgetdis

(int x)

return res;

}inline

void

push

(int k)

}//括起來

else

} sta1[

++top1]

= top;

}inline

void

pull()

flag =0;

}inline

void

solve

(int k,

int s,

int t)

if(s == t)

int mid =

(s + t)/2

;solve

(ls, s, mid)

;solve

(rs, mid +

1, t)

;pull()

;}intmain()

solve(1

,1, t)

;for

(int i =

1; i <= t;

++i)

puts

(ans[i]

?"yes"

:"no");

return0;

}

線段樹分治

首先,這裡的線段樹是狹義的線段樹。而線段樹分治是一種維護時間區間的資料結構,利用線段樹的分治性使時間複雜度為log loglo g級別。維護時間區間的資料結構有cdq分治 kd tree,那麼線段樹分治和它們的區別在 呢?其實,它就是用回退操作來實現可持久化,或者說是維護了操作會影響的時間區間。我們...

線段樹分治

day2模擬被完爆了w 來學一波線段樹分治 原來一直拿它口胡其實沒寫過。然鵝這個東西和線段樹的關係 就像點分治和點分樹一樣 並不用建出來 但遍歷順序是一致的 從上到下 從左兒子到右兒子 訪問 線段樹 的所有節點 每個節點表示乙個區間 當然維護的也是區間裡的值 這就要求我們維護的東西滿足區間加法 比如...

線段樹分治

這個演算法借助線段樹,然後事實上是通過遍歷線段樹進行分治。先用一種比較容易的方式來解釋吧。先回憶天天愛跑步一類的題目,大致就是在一棵樹上通過dfs,訪問到這個點的時候把這個點上的操作加入貢獻,離開的時候除去貢獻,然後就可以了。當我們發現乙個操作不能只用乙個點來表示,也即,兩個操作有交卻又不完全包含,...