區間分塊系列

2021-08-20 13:51:50 字數 2899 閱讀 7226

**:

很好的分塊知識講解。

可能涉及的幾個詞語解釋:

區間:數列中連續一段的元素

區間操作:將某個區間[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組...