P4198 樓房重建

Fire_Raku發表於2024-03-23

P4198 樓房重建

求從 \((0,0)\) 往上看能看到多少棟沒被擋住的樓房,帶修改。

對於帶修改的題目,我們需要快速維護,就需要用到資料結構。這時候透過直覺可以想到,問題是可以分為子問題然後合併得到的,所以我們考慮線段樹。

觀察到能被看到的樓房,從左到右斜率遞增,即我們需要維護斜率遞增的序列。

考慮子區間需要維護的資訊,發現該區間對其他區間的影響只跟其斜率最大值有關,所以一個是維護區間斜率最大值 \(\max\)。然後再維護一個區間答案 \(cnt\),表示從該區間的左端點能看到的樓房數量。

怎麼合併?記 \(u\) 為合併後的區間,\(ls\)\(rs\) 為左右區間。首先顯然左區間 \(ls.cnt\) 一定能貢獻到 \(u.cnt\) 中;右區間的答案顯然不能馬上得出,我們在把右區間細化成更小的問題,分類討論一下。記右區間的左區間為 \(rsl\),右區間的右區間為 \(rsr\)

如果 \(rsl.max<ls.max\),那麼 \(rsl\) 所有點都看不到,捨去,然後繼續考慮 \(rsr\)

否則 \(rsr\) 的貢獻只和 \(rsl.max\) 有關,可以發現貢獻即為 \(rs.cnt-rsl.cnt\),然後繼續考慮 \(rsl\)

這部分思考變成程式碼也很簡單,由於每次只會遞迴一個區間,複雜度單次是 \(O(\log n)\)

所以是 update 的同時套一個 \(O(\log n)\) 的合併區間,總複雜度是 \(O(n\log^2 n)\)

相關文章