這是線段樹的最後乙個知識點啦,終於不用繼續練碼量超大的線段樹了,因為筆者已經掌握了~
例題:n只奶牛構成了乙個樹形的公司,每個奶牛有乙個能力值pi,1號奶牛為樹根。
問對於每個奶牛來說,它的子樹中有幾個能力值比它大的。
solution:
做法是每個點開一棵線段樹,插入這個結點上的數,根節點就為這個點的序號。如果能把乙個結點的線段樹的資訊在合理的時間內全部插入(或者說「合併」)到它父親的線段樹中,就可以由下往上做一遍樹上遞推,這樣每個結點的線段樹都儲存的是整棵子樹的資訊了。
先放**吧:
int
merge
(int p,
int q,
int l,
int r)
int mid=
(l+r)
>>1;
t[p]
.lson=
merge
(t[p]
.lson,t[q]
.lson,l,mid)
; t[p]
.rson=
merge
(t[p]
.rson,t[q]
.rson,mid+
1,r)
;// t[p].sum=t[t[p].lson].sum+t[t[p].rson].sum;
t[p]
.sum+
=t[q]
.sum;
//they are the same
return p;
}
這個**還是相當好理解的,無需贅述。下面來證明一下複雜度。(我想了一中午終於想出乙個超簡潔的證明 —— 本來以為會很複雜的)
從**中可以看出合併兩棵樹的複雜度約等於這兩棵樹 重合 的結點數。假如初始所有的線段樹點數總和為 n
nn 。因為只要合併兩個節點,節點總個數就會少掉乙個,所以複雜度應該是 o(n
)o ( n )
o(n)
的。所以不是所有情況下的線段樹合併都是 o(n
log
n)
o ( n log n )
o(nlog
n) 的。
一般情況下我們只會對每個節點開動態開一條鏈,所以 n=n
log
nn = n log n
n=nlog
n,複雜度才會是 o(n
log
n)
o ( n log n )
o(nlog
n) 。
哇怎麼這麼短就寫完了 ……
update:「abc183f」confluence
題意:n個人,每個人屬於乙個班級ci,這些人會有些小團體(並查集)
兩種操作:
1 a b,將a所在的集體和b所在的集體合併
2 x y,問在x的集體中有多少人在y班
第一種做法啟發式合併,每次將點數較少的接到點數較多的上面。具體可以用map
mapma
p之類的stl
stlst
l來存集合。
第二種方法是線段樹合併。我們把乙個點所含的種類 [1,
2e5]
[1,2e5]
[1,2e5
] 看成乙個樹的結構,時間複雜度是嚴格 o(n
logn
)o(nlogn)
o(nlog
n)的。這個想法很妙。我們把乙個線性的單點合併放到動態開點線段樹上,這樣每次合併乙個點都刪除了乙個點,保證了演算法的高效。
第二種演算法的時間複雜度顯然是優於第一種的,第一種做法只能「單個搬運」,導致合併代價是不穩定的。而第二種則把若干子樹進行「整體搬運」,我們也通過理論證明了它時間上界和優秀的搬運性質(我們發現它在整個合併過程中沒有新建點)。
Hotel 線段樹區間合併模板題
有n個車位,一開始全是空的。m個要求,1 k,表示有k輛車要停,且位置要連續,找到編號最小的位置輸出,如果停不下,就輸出0 2 x k,表示從x位置開始,有連續k輛車開走。1代表有空位,0代表沒有空位。tr m left代表從左端點向右,連續1的區間長度,tr m right代表從右端點向左連續1的...
線段樹合併
做永無鄉的時候,以為是主席樹合併,後來感覺不對勁,唔。x和y是兩顆樹的根。這個演算法是從歸併演算法那引申的。實際運作的時候,考慮到了線段樹的本質 線段樹有效節點就是葉子節點。好像是句廢話。其實不是,這句話啟發我們並不需要合併一整棵樹,我們只需要處理好葉子節點,考慮把y樹合併到x上,那麼把y樹的葉子節...
線段樹合併
今天寫dsu on tree 的時候發現不會寫線段樹合併,於是滾來寫線段樹合併部落格 對於值域相同的兩個權值線段樹x xx和y yy 假設把y yy合併到x xx上 每個節點有兩種情況 其中至少有乙個節點沒有權值 x y x y x y 直接x x y x x y x x y x 0?y x x 0...