分塊 學習筆記

2022-03-26 10:28:08 字數 3394 閱讀 6293

前言:內容參考自感謝。

分塊,是一種優雅的暴力,它通過對數列分段,完成對數列一些區間操作和區間查詢的操作,是一種根號演算法。本文屬於分塊入門筆記,旨在零基礎的同學學會分塊。

1 建塊

在建塊伊始,我們需要完成一下幾個任務:

1.確定塊的大小

2.確定塊的數量

3.標記每個塊的左右邊界

4.標記每個元素所屬的塊

5.對塊內的元素進行預處理

1.1.塊的大小:為了保證時間複雜度最優,一般我們讓塊的大小為$\sqrt(n)$。寫起來就一句話:

int size=sqrt(n);

1.2.塊的數量:顯然為$n/size$。如果$n$不是完全平方數,則讓數量+1。

int tot=n/size;

if (n%tot) tot++;

1.3.塊的端點:對於每個塊的左右端點,顯然$l[1]=1,r[1]=block,l[2]=r[1]+1,r[2]=block*2,\cdots$,所以我們可以得到:

for (int i=1;i<=tot;i++)

l[i]=(i-1)*size+1,r[i]=i*size;

1.4.所屬的塊:同樣,我們可以得到每個元素所屬的塊:

for (int i=1;i<=n;i++) belong[i]=(i-1)/size+1;

1.5.初始化:根據題目的要求來進行操作。一般都有預處理區間和,或者對元素進行排序。

2.修改

分塊題常見的操作就是區間修改。對於修改區間$(x,y)$,我們一般這樣處理:

$(x,y)$的左邊和右邊可能不是屬於乙個整塊的。對於這些邊邊角角,我們暴力處理即可。

對於中間的整塊,我們考慮線段樹區間修改的思想:$lazy tag$。我們只需讓$add[i]+=k$即可,這就是分塊演算法較為高效的地方。

注意特判$belong[x]==belong[y]$的情況,此情況暴力處理即可。

**如下(以洛谷p2801為例):

inline void

update()

for (int i=x;i<=r[belong[x]];i++) a[i]+=k;//暴力處理

for (int i=l[belong[x]];i<=r[belong[x]];i++) d[i]=a[i];

sort(d+l[belong[x]],d+r[belong[x]]+1

);

for (int i=l[belong[y]];i<=y;i++) a[i]+=k;

for (int i=l[belong[y]];i<=r[belong[y]];i++) d[i]=a[i];

sort(d+l[belong[y]],d+r[belong[y]]+1

);

for (int i=belong[x]+1;i<=belong[y]-1;i++) add[i]+=k;//使用lazy tag。因為整塊內元素相對大小不變,所以無需進行重新排序。

}

3.查詢

如果是查詢區間和,那麼直接$ans+=sum[i]+add[i]*(r[i]-l[i]+1)$即可。如果是進行大小的查詢,因為塊內我們已經排過序,元素滿足單調性,所以我們可以進行二分查詢。

跟修改塊一樣,我們同樣是暴力處理邊邊角角,對於整塊二分查詢。

**如下(以洛谷p2801為例):

inline void

query()

for (int i=x;i<=r[belong[x]];i++) if (add[belong[x]]+a[i]>=k) ans++;//暴力

for (int i=l[belong[y]];i<=y;i++) if (add[belong[y]]+a[i]>=k) ans++;

for (int i=belong[x]+1;i<=belong[y]-1;i++)

ans+=res;

}printf(

"%d\n

",ans);

}

4.完整**

這裡以洛谷p2801為例。這題有乙個小細節,我們應該開兩個陣列:運算元組和原始陣列。因為排序會把原有元素的順序打亂,所以我們在暴力處理邊邊角角的時候要用到原始陣列。對於整塊處理因為是整體處理,所以直接用運算元組即可。

**:

#includeusing

namespace

std;

int a[1000005],d[1000005],l[1005],r[1005],belong[1000005],add[1005

];int

n,q,block,tot,x,y,k;

inline

intread()

while(isdigit(ch))

return x*f;

}inline

void

build()

inline

void

update()

for (int i=x;i<=r[belong[x]];i++) a[i]+=k;

for (int i=l[belong[x]];i<=r[belong[x]];i++) d[i]=a[i];

sort(d+l[belong[x]],d+r[belong[x]]+1

);

for (int i=l[belong[y]];i<=y;i++) a[i]+=k;

for (int i=l[belong[y]];i<=r[belong[y]];i++) d[i]=a[i];

sort(d+l[belong[y]],d+r[belong[y]]+1

);

for (int i=belong[x]+1;i<=belong[y]-1;i++) add[i]+=k;

}inline

void

query()

for (int i=x;i<=r[belong[x]];i++) if (add[belong[x]]+a[i]>=k) ans++;

for (int i=l[belong[y]];i<=y;i++) if (add[belong[y]]+a[i]>=k) ans++;

for (int i=belong[x]+1;i<=belong[y]-1;i++)

ans+=res;

}printf(

"%d\n

",ans);

}signed main()

return0;

}

分塊 學習筆記

當我們對於乙個很大陣列 1e5 進行區間修改和區間查詢時,我們會想到線段樹的 nlog n 的優秀效率。分塊 優雅的暴力!我們將區間分成每個大小為 s 的小塊,這樣我們的複雜度就會從 n 降到 frac n s 的效率。我們先將陣列分成長度為 s 小塊,用原下標除以 s 向上取整,就是他分塊後的小塊...

分塊學習筆記

在我不知道分塊以前,我一直以為分塊是乙個非常牛逼的東西。在我多次學習並且處於懵逼狀態的時候,我一直以為這輩子我不會分塊了。直到一天我學會了他。ps 乙個小建議,學習新知識要在上午哦 下面我就把剛剛學會的分塊做了一下總結。主要思想 分塊是乙個很暴力的演算法,跟普通的列舉暴力差不了多少。對於乙個長度為n...

分塊學習筆記

題面傳送門 演算法簡介 分塊主要是乙個修改,維護區間的東西,它可以做到一邊修改一邊查詢,區間修改 o sqrt n 區間查詢 o sqrt n 單點修改 o 1 單點查詢 o 1 演算法實現 初始化 首先要把基本陣列 以下簡稱 a 陣列,長度為 n 分成 m 塊。一般是分成 sqrt n 塊,當然也...