近期剛剛學了線段樹合併,感覺幾天後就忘了,所以寫出來方便以後複習
(如有雷同,那就是我在網上借鑑的了。。。。。。。。。)
顧名思義,線段樹合併就是把兩棵線段樹合併到一塊,廢話,但是首先要考慮乙個問題,什麼樣的線段樹可以合併,比如一棵表示區間最大值,另一棵表示區間最小值,那這兩棵線段樹顯然不能合併,於是會發現,大部分維護區間性質(如和,積,最大值,最小值,異或和等等)的線段樹一般都不能合併,那麼什麼樣的可以呢————權值線段樹。
權值線段樹,基於普通線段樹,但是不同。線段樹合併主要有兩種:舉個栗子:對於乙個給定的陣列,普通線段樹可以維護某個子陣列中數的和,而權值線段樹可以維護某個區間內陣列元素出現的次數。
在實現上,由於值域範圍通常較大,權值線段樹會採用離散化或動態開點的策略優化空間。單次操作時間複雜度\(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有一顆樹...