1. 程式人生 > >【JZOJ 3397】 雨天的尾巴 線段樹合併

【JZOJ 3397】 雨天的尾巴 線段樹合併

Description

一棵樹,有若干操作,每個操作包含三個數x,y,z,表示在樹上從x到y的路徑上的所有點的z顏色的權值+1。所有操作結束後詢問每個點權值最大的顏色是什麼。

對於100% 的資料,1 <= n,m <= 100000; 1 <= x, y <= n; 1 <= z <= 10^9

Analysis

這道題是用的線段覆蓋的思想。只不過放在樹上。
我們想樹上的兩個點u,v之間的路徑可以由(root,u)+(root,v)-(root,lca(u,v))-(root,fa(lca(u,v)))構成。
所以線段覆蓋放在樹上可以轉化成每個點資訊的合併。即從下往上做,每個點的資訊與其所有子節點合併。
在這裡,顯然每個點的資訊就是顏色權值了。
怎麼記錄?權值線段樹!空間會爆?首先把操作的z離散化,然後動態開節點!
然後就是線段樹的操作了。
由於本人弱,這是第一道線段樹合併的題。
兩棵線段樹合併,對應的節點搜下來,若一個為空就返回另一個,否則合併節點的左右子節點。合併到葉子節點就將權值直接“+”一下(這個+指滿足結合律的所有運算)
2017.5.8更
發現並不會證線段樹合併複雜度
上網搜到了Po姐的證明,怒%一發

Po姐:
我定義一棵線段樹的勢為節點個數
那麼合併兩棵線段樹的代價為 兩棵線段樹的勢和-新線段樹的勢
然後一開始所有線段樹的勢之和為O(nlogn)
一個線段樹上有logn個節點
合併到最後有nlogn個節點
所以最後的勢能差是O(nlogn)
完事了

太強啦><
然後這題就能時間空間都是nlogn了

Key Code

我自己原始的code太噁心,於是觀摩了一下alan_cty的,果然大神就是大神,程式碼簡潔明瞭又實用%%%

int merge(int x,int y,int l,int r)
{
    if(!x || !y) return
x+y; if(l==r) { a[x].mx+=a[y].mx; return x; } int mid=(l+r)>>1; a[x].l=merge(a[x].l,a[y].l,l,mid); a[x].r=merge(a[x].r,a[y].r,mid+1,r); a[x].mx=max(a[a[x].l].mx,a[a[x].r].mx); return x; }