真,頂級毒瘤題目,浪費我至少一天。
首先不難想到對於修改,有一個暴力序列線段樹做法:
-
如果當前區間的最大值 \(\le x\),那麼直接返回,無法進行修改。
-
如果當前區間的最小值 \(\ge x\),那麼區間減,打上懶標記即可。
-
否則,就暴力修改左右兒子然後 \(\texttt{pushup}\)。
顯然,由於正常人都會造資料,使得每次修改都被無限細分到葉子節點,所以必然會被卡。
我們發現,這裡的瓶頸在於我們不能確定到底該如何修改。
考慮對權值進行分塊,每個塊維護一顆序列線段樹。
但是這裡權值太大,就算分出來,也完全無法構造線段樹。
所以考慮倍增分塊(當然,這只是這樣分塊的其中一個原因)。
我們定義一個進位制 \(B\),將 \([B^i,B^{i+1})\) 分成一塊, 然後,對於每塊如上建立線段樹。
修改的時候,對於塊 \(i\)(假設其左右端點分別為 \(l_i,r_i\)):
-
\(l_i\ge x\),顯然,這整個塊根本就無法修改。
-
\(r_i< x\),這個稍微處理一下就好。顯然這個塊內所有下標在 \([l,r]\) 以內的數都要修改,這個可以直接透過懶標記解決。但是考慮到你減了之後可能會改變你所在的塊,所以我們對於那些整個區間減了之後都還在這個塊內的打上懶標記,其餘的就暴力修改,然後暴力變塊。
-
\(l_i<x\le r_i\),即有些修改有些不改。這個就同我們最開始的暴力線段樹修改,不在闡述,注意減了之後變塊的問題就行。
然後我們來考慮一下時間複雜度的問題。
首先是變塊,由於我們精準的找到了要變的位置,加之還有插入線段樹,所以變塊只會產生 \(\mathcal{O}(n\log_BV\log_2n)\) 的時間損耗,問題不大。
但是一些線段樹內部遞迴消耗還值得我們去思考,由於作者暫時還沒有搞得很清楚,所以先咕咕咕。
當然,這樣還沒有結束,你會發現你光榮 \(\texttt{MLE}\) 了。
仔細思考可以發現,你線段樹是4倍空間,原因是你分的太細了,要一直分到葉子節點。
那我不分這麼細就行了嘛。
所以考慮另外一個技巧:底層分塊。其實有點像閾值分治,就是如果你線上段樹上遞迴到的當前區間長度小於一個閾值 \(K\),那就把這個區間當作葉子節點暴力列舉處理。
當然,這樣寫的一個非常重要的細節是,你每次暴力進入區間計算時,記得把這個點的標記暴力傳到這個區間上。
對於具體引數,\(K=32,B=16\),即可。
\(\texttt{Submission}\)