李超線段樹
因為太弱了,所以只會用單調隊列、CDQ分治、平衡樹來維護凸殼,然后被\(zjp\_shadow\)聚聚在博客底下給D了一頓,所以辣雞yyb就來學一下了。
(似乎整個機房就我不會了)
首先先明白這個東西在干啥
你要資磁動態維護一個平面直角坐標系,資磁在中間插入一條線段,資磁詢問與\(x=x0\)這條直線相交的所有線段中,交點的\(y\)軸坐標的最大(小)值。
我們要維護的東西是這個:維護這個區間內的所有直線中,從上往下能夠看到的最長的那個線段,也就是沒有被其他直線覆蓋長度最大的段。
考慮怎么插入一條直線,假設它當前處理到了某個區間:
- 如果這個區間沒有記錄最長的線段,那么直接把這個區間記錄的線段修改為這條線段,然后返回。
- 如果當前線段在這個區間內已經被這個區間內的最長線段為覆蓋,那么直接\(gg\),返回就好了。
- 反過來,如果完全覆蓋了之前記錄的線段,那么直接賦值、返回。
- 否則和已經記錄的直線有交,判斷哪根線段覆蓋的區域較長,把這個區間記錄的值給修改一下,然后把短的那一半丟下去遞歸。
這樣子復雜度是啥呢?顯然維護復雜度看起來不太對的就是最后一項,但是不難證明每次遞歸下去直線長度都至少要減少一半,所以這個東西的復雜度就是一個\(log\)的。
至於詢問?那就是單點詢問啦,在線段樹上一直走到這個單點為之,把路徑上所有記錄的線段拿出來取一個\(max\)就好啦。
比如BZOJ1568就是模板題QwQ。
當然了,上面這題是每次插入一條直線,這樣子只需要一個\(log\),如果每次插入一條線段的話還是要稍微變一下的,即要先找到對應的區間再在這個區間內插入這個線段,這樣子復雜度是兩個\(log\)的,代碼戳這里。
然后\(zjp\)說可以用李超線段樹直接維護斜率優化,想了想的確可以,我這里隨便搬一道題目過來。
[HNOI2010]玩具裝箱
首先寫暴力\(O(n^2)\)的轉移,設\(S_i\)是\(C_i\)的前綴和。
然后把式子拆開,和\(j\)無關的直接移出去,只和\(j\)相關的放在一起,同時和\(i,j\)相關的放在一起。
那么分類之后就是這樣的:
和\(j\)無關的:\(i^2-2i+1+S_i^2+L^2-2LS_i+2iS_i-2iL-2S_i+2L\)
只和\(j\)有關的:\(f[j]+j^2+2j+S_{j}^2+2LS_j+2jS_j+2jL+2S_j\)
同時和\(i,j\)相關的:\(-2ij-2S_iS_j-2iS_j-2jS_i=-2(i+S_i)(j+S_j)\)
一共\(22\)項,似乎沒有什么問題。(其實可以直接令\(M_i=i-1+S_i-L\),但是為了鍛煉拆式子能力就這樣吧......算了,我編不下去了.....)
那么把和\(j\)無關的部分記做\(pre[i]\),只和\(j\)有關的記做\(y[j]\),\(j+S_j\)記做\(x[j]\),\(2(i+S_i)\)記做\(k_i\)。
那么轉移方程可以改寫成:
而\(k_i\)是一個常數,我們把后面這個式子理解為一個一次函數\(y=kx+b\)的形式,得到\(b=y-kx\)。
什么意思呢?平面上有若干個點\((x[j],y[j])\),你要過這些點畫一條斜率為\(k_i\)的直線,使得其截距最小。
不難發現滿足條件的\(j\)一定在下凸殼上。
這里有個很優秀的性質,也就是\(k_i,x[j]\)都是單增的。
這樣子凸殼可以直接用單調隊列維護,而取最優值只需要每次找到凸殼左側最優位置就好啦。
上面是直接用斜率優化,然后單調隊列維護凸殼的做法(我直接從別的文章里蒯過來的)
那么我們把那個式子換一下,把\(y[j]\)寫成\(b[j]\),\(-x[j]\)寫成\(k[j]\),
那么轉移就是
於是問題變成了,平面上有若干條直線,現在你要詢問在\(x=2(i+S_i)\)處的最小值,所以可以直接使用李超線段樹來維護。(雖然復雜度多了一個\(log\))。
