前言:內容參考自感謝。
分塊,是一種優雅的暴力,它通過對數列分段,完成對數列一些區間操作和區間查詢的操作,是一種根號演算法。本文屬於分塊入門筆記,旨在零基礎的同學學會分塊。
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 voidupdate()
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 voidquery()
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為例。這題有乙個小細節,我們應該開兩個陣列:運算元組和原始陣列。因為排序會把原有元素的順序打亂,所以我們在暴力處理邊邊角角的時候要用到原始陣列。對於整塊處理因為是整體處理,所以直接用運算元組即可。
**:
#includeusingnamespace
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 塊,當然也...