前言:
首先,我們來考慮這樣一個模型:有一段連續的序列a[1]~a[n],然后現在我們需要執行幾類操作:
出題人: 求出其中一段區間的和
智商180的某寶寶:哎呀,你怎么這么傻,直接記錄這個序列的前綴和不就得了?
記錄a[1]~a[i]的和為sum[i],然后顯然有sum[i+1]=sum[i]+a[i+1],我們要求a[l]~a[r]就直接sum[r]-sum[l-1]唄。
出題人:區間加上某個值
由於某寶寶是大佬,兩分鍾后:我會一種叫線段樹的東西(一種樹形結構,可以維護區間求和和單點修改的優秀數據結構)!!!
出題人:查詢一段區間上有多少個數<k (k>0且給定)
某寶寶:
出題人:對了,忘了告訴你了,k每次都不一樣,還有,極其的多。另外,為了防止裝*不讓寫平衡樹(另一種能干很多事情的優秀數據結構)+線段樹,占用空間不能超過****Mb
某寶寶:
下面就分享一個菜鳥也能懂得算法:分塊
分塊,顧名思義,就是把一段序列分成一小塊一小塊得來處理,維護。
我們把一段當成一個整體,只記錄維護整體的有關信息,就是分塊。
首先,對於前言說得那道題,很朴素的做法就是:
1.從詢問區間的l到r掃過去,每回加上掃到的值,即$ans=\sum^{r}_{i=l} a[i]$
2.直接把$a[i]$重新賦值不就得了 a[i]=newa[i];
3.從詢問區間的l到r掃過去,每回遇到<k的位置,答案+1
沒錯,這種做法很傻是不是?
但是,分塊就是在這個基礎上暴力優化的!!!
假設我們總共的序列長度為n,然后我們把它切成$\sqrt{n}$塊,然后把每一塊里的東西當成一個整體來看,
現在解釋幾個本文用到的術語:
完整塊:被操作區間完全覆蓋的塊
不完整塊:操作區間不完全覆蓋的塊
然后我們先看看怎么得出答案:
1.對於完整的塊,我們希望有個東西能直接找出這整個塊的和,於是每個塊要維護這個塊的所有元素的和。
.對於不完整塊,因為元素比較少(最多有 總數n / 塊數 = $\sqrt{n}$ 個) 這時候當n=1000000的時候最多有1000個,對比一下,我們可以直接暴力掃這個小塊統計答案,
.小技巧:如果這個不完整塊被覆蓋的長度>塊維護的長度的一半,何不用這個塊的和-沒有被覆蓋的元素的值呢?
2.這里,我們換種思路,記錄一個lazy 標記(為什么用lazy,因為我很懶),表示整個塊被加上過多少了,
.對於完整塊,我們直接lazy+=加上的數x,塊內的和ans+=x*元素個數(因為每個元素都被加上了x)
.對於不完整塊,直接暴力修改就好了,順便可以把lazy標記清了。
3.哎呀,這個有點難度啊,
.要在每個完整塊內尋找小於一個值的元素數,
顯然我們不得不要求塊內元素是有序的,這樣就能用二分(快速在一個有序的序列里查詢的一個算法),對塊內查詢。
.不完整的塊暴力就好
.這樣的話需要提前對每塊里面的元素做一遍排序就好.
.但是當有修改的話,因為整個塊同時加上(減去)一個數,每個數的相對大小是不會變的,但是如果是不完全塊就會改變,這樣的話,還是因為元素個數小,重新新排一下不就得了?
然后,這道題就用了一種看似高大上的方法做完了……比之前傻傻的暴力是不是好看很多呢?
在很多地方,我們可以運用到分塊的思想,化零為整,把維護每個數的值變成維護一些整體的值,
一個很常見的例子就是:為什么班主任要給班里分組?因為他可不想收作業或者回執的時候一個一個收啊qwq交給組長然后組長在交給老師多好啊qwq
這樣,我們就不用一個一個的找了qwq
然后就講完基礎了。
稍稍進階:
其實,每一塊可以維護的不止上面說的那幾種東西,我們可以維護當前區間最大公約數是多少,最大異或和是多少……
客觀的來說,分塊是可以維護很多的,只要想出來怎么預處理,對於有修改的模型怎么維護,統計答案的時候怎么累加就行。
希望對大家有所幫助。