**:
很好的分塊知識講解。
可能涉及的幾個詞語解釋:
區間:數列中連續一段的元素
區間操作:將某個區間[a,b]的所有元素進行某種改動的操作
塊:我們將數列劃分成若干個不相交的區間,每個區間稱為乙個塊
整塊:在乙個區間操作時,完整包含於區間的塊
不完整的塊:在乙個區間操作時,只有部分包含於區間的塊,即區間左右端點所在的兩個塊
例題1:
給出乙個長為n的數列,以及n個操作,操作涉及區間加法,單點查值。
這是一道能用許多資料結構優化的經典題,可以用於不同資料結構訓練。
數列分塊就是把數列中每m個元素打包起來,達到優化演算法的目的。
以此題為例,如果我們把每m個元素分為一塊,共有n/m塊,每次區間加的操作會涉及o(n/m)個整塊,以及區間兩側兩個不完整的塊中至多2m個元素。
我們給每個塊設定乙個加法標記(就是記錄這個塊中元素一起加了多少),每次操作對每個整塊直接o(1)標記,而不完整的塊由於元素比較少,暴力修改元素的值。
每次詢問時返回元素的值加上其所在塊的加法標記。
這樣每次操作的複雜度是o(n/m)+o(m),根據均值不等式,當m取√n時總複雜度最低,為了方便,我們都預設下文的分塊大小為√n。
#include
#include
using
namespace
std;
int n, blo;
int v[50005], bl[50005], atag[50005];
void add(int a, int b, int c)
int main()
for (int i = 1; i <= n; i++)bl[i] = (i - 1) / blo + 1;
for (int i = 1; i <= n; i++)
return
0;}
例題2:
給出乙個長為n的數列,以及n個操作,操作涉及區間加法,詢問區間內小於某個值x的前驅(比其小的最大元素)。
n<=100000其實是為了區分暴力和一些常數較大的寫法。
不過這題其實想表達:可以在塊內維護其它結構使其更具有拓展性,比如放乙個 set ,這樣如果還有插入、刪除元素的操作,會更加的方便。
分塊的除錯檢測技巧:可以生成一些大資料,然後用兩份分塊大小不同的**來對拍,還可以根據執行時間嘗試調整分塊大小,減小常數。
#include
#include
#include
#include
#include
using
namespace
std;
int n, blo;
const
int maxn = 100000 + 5;
int v[maxn], bl[maxn], atag[maxn];
setst[maxn];
void add(int a, int b, int c)
if (bl[a] != bl[b])
}for (int i = bl[a] + 1; i <= bl[b] - 1; i++)
atag[i] += c;
}int query(int a, int b, int c)
return ans;
}int main()
for (int i = 1; i <= n; i++)
for (int i = 1; i <= n; i++)
return
0;}
例題3:
給出乙個長為n的數列,以及n個操作,操作涉及區間開方,區間求和。
稍作思考可以發現,開方操作比較棘手,主要是對於整塊開方時,必須要知道每乙個元素,才能知道他們開方後的和,也就是說,難以快速對乙個塊資訊進行更新。
看來我們要另闢蹊徑。不難發現,這題的修改就只有下取整開方,而乙個數經過幾次開方之後,它的值就會變成 0 或者 1。
如果每次區間開方只不涉及完整的塊,意味著不超過2√n個元素,直接暴力即可。
如果涉及了一些完整的塊,這些塊經過幾次操作以後就會都變成 0 / 1,於是我們採取一種分塊優化的暴力做法,只要每個整塊暴力開方後,記錄一下元素是否都變成了 0 / 1,區間修改時跳過那些全為 0 / 1 的塊即可。
這樣每個元素至多被開方不超過4次,顯然複雜度沒有問題
#include
#include
#include
#include
using
namespace
std;
int n, blo;
const
int maxn = 50000 + 5;
int bl[maxn];
int v[maxn], sum[maxn], flag[maxn];
void solve_sqrt(int x)
}void add(int a, int b, int c)
if (bl[a] != bl[b])
for (int i = (bl[b] - 1)*blo + 1; i <= b; i++)
for (int i = bl[a] + 1; i <= bl[b] - 1; i++)
solve_sqrt(i);
}int query(int a, int b)
int main()
for (int i = 1; i <= n; i++)
for (int i = 1; i <= n; i++)
return
0;}
區間處理之分塊
分塊這種思路很常見,就是把乙個數列劃分成k塊,然後在塊的基礎上進行操作。假如每塊的大小為magic,那麼長度為n的數列則一共會劃分成ceil n magic 塊。這樣會有一些性質 1.原數列第i個的塊號為i magic,是塊內的第i magic個 不過這一條沒有用 2.假如i magic 0,說明i...
分塊 區間眾數(金牌導航 分塊 1)
給出乙個數列,和若干詢問,每個詢問讓你求乙個區間內的眾數 6 3 1 2 3 2 1 2 1 5 3 6 1 51 2 11 n 4 1 04,1 m 5 104 1 a i 10 91 leqslant n leqslant 4 times 10 4,1 leqslant m leqslant 5...
Codeup分塊思想 區間查詢
時間限制 2 sec 記憶體限制 32 mb 提交 198 解決 107 提交 狀態 討論版 命題人 外部匯入 食堂有n個打飯視窗,現在正到了午飯時間,每個視窗都排了很多的學生,而且每個視窗排隊的人數在不斷的變化。現在問你第i個視窗到第j個視窗一共有多少人在排隊?輸入的第一行是乙個整數t,表示有t組...