先來考慮一個單點修改、區間查詢的問題,然後來個簡單的想法:如果把數列切成好幾塊,然後在修改之後更新那一塊的答案,如果區間查詢的範圍涵蓋一整塊,那麼這一塊的答案就不必重新算,如果有包含到一塊中的一小段,那這段就暴搜,這樣會不會比較好?
數列
格子中的數字是那一塊的編號,如果數列中的元素是從
在初始化時,計算出每一塊的答案,例如如果查詢求的是區間和,那就儲存每一塊的數字和、求的是最大值,就儲存每一塊裡的最大值。
單點修改的時候,在修改完後,更新那一塊的答案,如果求的答案是區間和,那麼只要把那一塊的和扣掉原來數字,再加上新的數字,這種「可返回」的東西,可以做
區間查詢的時候,如果有完整的一塊被涵蓋,那就直接
接下來來討論一下
要讓等號成立的話,就必須要
如果要做區間修改的話,用剛剛的方式會變成要對每個單點修改一遍、再去更新每一塊的答案,這樣複雜度是
觀察一下,如果整塊都一起被修改會發生什麼事?
由此可知,如果整塊一起變動,那我們可能可以推算出我們想要的答案會如何改變,此時我們就不要浪費時間去修改全部的數字,多開一個變數來儲存這一塊都被改成什麼數字或被多加上多少,這就稱為懶人標記。至於邊邊不到一整塊的部分,就一樣暴力處理。
如果某一塊已經有懶人標記,也就是已經被整塊一起修改過,那麼如果之後要再變動這一整塊,只要修改懶人標記就好;但如果之後是只變動部分呢?如果修改是加值的話,硬加上去,不理會懶人標記是可以的,但如果修改是改值,而不理會懶人標記,那麼在查詢時,這個元素的值還是會被覆蓋掉,所以就必須要依懶人標記所記錄的修改,去修改那一塊裡的每一個元素,再去修改剛剛想改的那一部分了。
打一塊的懶人標記是