筆記 線段樹合併

2022-02-03 04:10:00 字數 1696 閱讀 4906

近期剛剛學了線段樹合併,感覺幾天後就忘了,所以寫出來方便以後複習

(如有雷同,那就是我在網上借鑑的了。。。。。。。。。)

顧名思義,線段樹合併就是把兩棵線段樹合併到一塊,廢話,但是首先要考慮乙個問題,什麼樣的線段樹可以合併,比如一棵表示區間最大值,另一棵表示區間最小值,那這兩棵線段樹顯然不能合併,於是會發現,大部分維護區間性質(如和,積,最大值,最小值,異或和等等)的線段樹一般都不能合併,那麼什麼樣的可以呢————權值線段樹。

權值線段樹,基於普通線段樹,但是不同。

舉個栗子:對於乙個給定的陣列,普通線段樹可以維護某個子陣列中數的和,而權值線段樹可以維護某個區間內陣列元素出現的次數。

在實現上,由於值域範圍通常較大,權值線段樹會採用離散化或動態開點的策略優化空間。單次操作時間複雜度\(o(logn)\)

權值線段樹的節點用來表示乙個區間的數出現的次數

例如:數1和2,分別出現3次和5次,則\(l==r==1\)的點記錄3,\(l==r==2\)的點記錄5,1和2的父節點記錄它們的和8.

線段樹合併主要有兩種:

我還沒寫到過第二種的題,所以先來看第一種。

例如這道題,[poi2011]rot-tree rotations

先看到前序遍歷,可能會有點懵,但是再仔細看的話會發現,只有葉子節點有值,也就是說,前序遍歷的深層意思就是,對於每棵子樹而言,會先輸出左子樹然後再輸出右子樹中的值。

我們還可以再想,逆序對的情況可能會有以下三種

#include#include#includeconst int lqs=2e5+10;

int cnt,lc[lqs*20],rc[lqs*20],siz[lqs*20];

void update(int &rt,int l,int r,int val)

int mid=l+r>>1;

if(val<=mid)update(lc[rt],l,mid,val);

else update(rc[rt],mid+1,r,val);

siz[rt]=siz[lc[rt]]+siz[rc[rt]];

}long long tot1,tot2,ans;

int merge(int lt,int rt,int l,int r)

tot1+=1ll*siz[lc[lt]]*siz[rc[rt]];

tot2+=1ll*siz[lc[rt]]*siz[rc[lt]];

int mid=l+r>>1;

lc[lt]=merge(lc[lt],lc[rt],l,mid);

rc[lt]=merge(rc[lt],rc[rt],mid+1,r);

siz[lt]+=siz[rt];

return lt;

}int dfs()else update(p,1,lqs,val);

return p;

}int main()

來證明一下:

假設我們會加入\(k\)個點,由上面的結論,我們可以推出最多要新增\(klogk\)個點。

而正如我們所知,每次合併兩棵線段樹同位置的點,就會少掉乙個點,複雜度為\(o(1)\),總共\(o(klogk)\)個點,全部合併的複雜度就是\(o(klogk)\)

這個應該很好理解

線段樹合併的應用還是很廣泛的,比如用於樹上差分啊或者動態維護一些東西,做題時會體會到的。

線段樹合併複習筆記

線段樹合併和主席樹不同的是。主席樹的節點還要依賴之前的樹結構。而線段樹合併是若干個分離的線段樹合併起來,在建立節點的時候有些不同。按照dfs的順序處理。顯然乙個子樹內的交換只會影響該子樹,所以可以貪心得對於每個子樹決定是否交換。算到葉子節點的時候新建一顆log loglo g個節點的權值線段樹。合併...

線段樹合併學習筆記

線段樹合併對一整個樹做完時間空間複雜度是n log nn log n nlog n的,套點其他什麼東西複雜度就上去了 動態開點的話注意 空間 樹上主席樹啟發式合併的話不 空間是兩個log的,容易被卡,比如這題 我就被卡了 悲 當然線段樹合併貌似總是可以被spl ay splay spla y啟發式合...

線段樹合併學習筆記

就是兩顆線段樹合成乙個線段樹 那合成的線段樹是適合所有線段樹嗎 當然不是,是動態開點線段樹 這裡建n個節點的時候,每個節點建一棵樹 而且要按照一定的形態建立一條鏈 就是說如果最終形態是有n個數字的樹,那你初始化的那一條鍊子一定是這顆樹上扣下來的 這樣才方便合併 最重要的操作 流程 如果x和y有一顆樹...