EI 隊長不務正業
又是一個(對於我來說)理性愉悅的東西,大概幾年之內(或者這輩子?)都不會寫這東西的代碼……
1 概述
自然的想法是維護這些函數取 \(\max\) 之后得到的分段函數,但這就帶來一個問題:分段函數最多能分多少段。
只考慮任意兩個函數,如果段數不超過 \(s+1\) ,那么總的分段函數的段數也會有一個比較緊的界。
考慮分段函數每一段屬於哪個函數,設為 \(\{a_i\}_{i=0}^m\) 。如果存在 \(x,y\) 使得 \(a\) 中存在長度超過 \(s+1\) 的 \(x,y\) 交替子序列,那就與 \(\max(f_x,f_y)\) 段數不超過 \(s+1\) 矛盾。
於是引入
1.1 Davenport-Schinzel 序列
那么段數的上界就和 \(DS(n,s)\) 的長度有關。
有一個牛逼定理:
於是我們驚喜地發現,段數竟然幾乎是線性的。
1.2 DS 序列與問題的聯系
為什么 \(s\) 次分段函數的階是 \(s+2\) 呢?如果定義域無交那么顯然只有 2(還是 3 ?)段,否則可能在兩者的定義域中已經獲得了 \(s\) 個交點,而在邊界還會獲得兩個。
2 函數集合維護
有了上面那個結論之后這里顯得比較簡單。
如果函數初始給定,那么直接分治合並所有函數,然后詢問的時候二分 \(x\) 在哪一段即可(當然如果可以離線就沒必要二分了)。預處理復雜度 \(O(\lambda_s(n)\log n)\) ,詢問復雜度 \(O(m\log n)\) 。
如果是一邊添加函數一邊詢問:用經典的二進制分組算法(也就是不完全的分治)處理修改,但詢問要在 \(O(\log n)\) 個分段函數上二分,復雜度變為 \(O(m\log^2 n)\) 。
但是容易發現可以用分散層疊來優化二分的過程,在新合並出一個 \(2^k\) 的段時把 \(2^{k},2^{k-1},\cdots,1\) 的分散層疊重構,而重構的復雜度並不是瓶頸。於是復雜度仍然是 \(O(m\log n)\) 。
2.3 應用
2.3.1 維護分段一次函數最值
直接做就行了。
2.3.2 動態規划問題
令 \(f_j(x_i)=a_j+w_{j,i}\) ,如果 \(f_j,f_k\) 交替階數較低,那就可能可以大力維護分段函數。
通常有兩類做法:直接發現 \(f_j\) 是關於 \(x_i\) 的一次函數,或者發現有決策單調性。
如果是一次函數那就直接做,否則決策單調性一般會滿足四邊形不等式,就有
也就是 \(f_{i+1}-f_i\) 單調遞增,所以 \(\max(f_i,f_{i+1})\) 在 \((i+1,n]\) 上階為 1 ,所以 \(i<i_0\) 的函數取 \(\max\) 在 \([i_0,n]\) 上是 \(s=1\) 階交替。
怎么求分段點就是另外一回事了……?
3 詢問點單調遞增
3.1 Kinetic Tournament 樹
用線段樹維護區間中對於當前 \(x\) 取到最大值的函數是哪個,而 \(x\) 變化的時候暴力修改。
每個點的分段個數是 \(\lambda_s(n)\) ,再帶上 \(\log n\) 層,再加上修改的時候是暴力從根節點往下 dfs 修改,所以復雜度 \(O(\lambda_s(n)\log^2 n+q)\) 。
3.2 帶修改函數序列最值問題
現在可以支持單點修改函數序列。
由於 \(x\) 單調遞增,所以一個位置仍然可以看做只有一個函數,只不過這個函數分段。於是復雜度變為 \(O(\lambda_{s+2}(n+m)\log^2 n+q)\) 。
實現的時候當然沒有必要關心函數具體怎么分段,修改的時候暴力即可(吧)。所以該在線還是在線。
4 線性情況的擴展
一次函數比較優美,因為如果只有 \(x\) 單調遞增的話那么一定是從斜率較小的函數切換為斜率較大的函數。
4.1 包含兩類區間修改的序列最值問題
另外還要求 \(x>0\) 。為了方便,再加上 \(k_i,b_i,c>0\) 。
類似於每個位置有一個函數 \(k_ix+b_i\) ,一操作讓區間的 \(x\) 增加,二操作看起來比較迷惑,但是被二操作完全覆蓋的區間中 \(k,b\) 的大小關系都沒有變化,並且好像可以看做 \(x\) 也沒有變化?
考慮 \(k_1x+b_1\) 和 \(k_2x+b_2\) ,且 \(b_1>b_2,k_1<k_2\) ,在同時經過二操作之后會發生什么。原來是還需要 \(x\) 增加 \({b_1-b_2\over k_2-k_1}\) 才會使得大小關系改變,而現在竟然完全沒有變化。
於是有這么一個做法:線段樹維護區間的 \(x,k,b\) 發生了什么變化,以及最大值在哪里取到,以及 \(x\) 再整體增加多少就會使得最大值切換。
一操作時如果沒有切換最大值那就無事發生,否則遞歸下去換;二操作一定不會切換最大值,也不會改變“還要多久才切換”。而 pushup 是簡單的。
唯一的問題是一操作中的“遞歸下去換”,這個復雜度是什么。
4.1.1 復雜度分析
如前所說,切換最大值的時候一定是從斜率小的換成斜率大的。
那么一次對 \(v\) 的切換會使得 \(\Phi\) 減小 \(d(v)\) ,但同時又可能增加 \(d(fa_v)\) ,所以至少減小 1 。
兩種操作會帶來什么呢?只有被碰到的節點可能會被丟進 \(\mathcal {P}\) 中,只會碰到 \(O(\log n)\) 個點,所以只會增加 \(O(\log^2 n)\) 的勢能。
初始還有 \(O(n\log n)\) 的勢能。
所以總復雜度 \(O(n\log^2 n+m\log^3 n+q\log n)\) 。
4.1.2 特殊情況
“區間增加公差為正的等差數列”,即保證 \(k_i\) 單調遞增。
那么切換就只能從左兒子切換到右兒子。另外,對於 \(x\) ,如果它子樹中的 \(\max\) 已經在右兒子了,那么打 \(tag\) 的時候即使 \(ls\) 被切換了, \(x\) 也不會被切換,因為右邊加的比左邊多。
所以可以直接設 \(\Phi\) 為“\(\max\) 位於左子樹”的點數,比上面少一個 \(\log\) ,復雜度 \(O(n\log n+m\log^2 n+q\log n)\) 。
4.2 例題
於是獲得了好多個 $\log $(
亂做?
4.2.1 最大連續子段和
區間加正數,區間最大子段和。
於是要維護 \(sum,lmax,rmax,totmax\) 。它們滿足
全都是關於區間加的一次函數。
如何構造一個神奇的勢函數,使得切換一定會消耗勢能呢?顯然還是應該和“斜率比自己選的直線的斜率大的個數”有關。要考慮到給自己切換的時候可能也會影響父親。
當 \(lmax\) 切換,可能會使得自己的斜率比父親的 \(lmax,totmax\) 大; \(rmax\) 同理;\(totmax\) 則只會使得自己的斜率比父親的 \(totmax\) 大。所以要求 \(l_d>l_{d-1}+t_{d-1},r\cdots,t_d>t_{d-1}\) 。於是可以令 \(t_d=d,l_d={d(d-1)\over 2}\) 。
初始勢能:給 \(lmax\) 的勢能貢獻 \(O(n\log^2 n)\) ,給 \(totmax\) 的勢能貢獻 \(O(n\log n)\) 。
一次修改:給 \(lmax\) 的勢能貢獻 \(O(\log^3 n)\) ,給 \(totmax\) 的勢能貢獻 \(O(\log^2 n)\) 。
於是分析出了復雜度 \(O(n\log^3 n+m\log^4 n+q\log n)\) (切換一次還會乘上額外的 \(O(\log n)\) )。
不過注意到 \(lmax\) 的轉移點一定是單調地從左往右,所以 \(lmax,rmax\) 的總切換次數其實僅僅是 \(O(n\log n)\) ,貢獻到 \(totmax\) 的勢能上就是 \(O(n\log^2 n)\) ,然后再把 \(totmax\) 的復雜度分析抄過來,就得到 \(O((n+m)\log^3 n+q\log n)\) 。
看着還行
如果沒有“轉移點單調往右”的性質那就只能用上面那個復雜度分析了。
4.2.2 henry_y 的數列
逐漸陰間。
區間加二次函數?換成把 \(i,i^2\) 看做系數, \((a,b)\) 看做變量。因為 \(i,i^2\) 單調遞增,而 \(a,b\ge 0\) ,所以區間打 \(tag\) 時最小值位置一定是從右往左走。
什么時候會切換呢?假設左子樹 \(\min\) 在 \(A_i\) ,右子樹在 \(A_j\) ,且 \(A_j<A_i\) ,那么
即 \((a,b)\) 走到某個半平面內的時候才會切換。
這就比較麻煩了,切換的條件從一維變成二維的了。
頭鐵,直接維護子樹中的半平面交。注意到直線斜率 \(i+j\) 滿足 \(ls< x< rs\) ,所以可以直接可持久化平衡樹維護半平面交,合並的時候二分。
另外,仍然注意到右邊加的比左邊多,所以可以令 \(\Phi\) 為 “\(\min\) 在右子樹取到的點數” ,省一個 $\log $ (?)
復雜度 \(O((n+m\log n)\log^2 n+q\log n)\) 。