分块
思想:将长区间分为小区间,维护区间值,降低时间复杂度
通常将区间分为 n \sqrt{n} n块,这样可以保证块数以及块数内的元素都较少,对区间內部的整块进行整体的操作,对区间边缘的零散块单独暴力处理。
时间复杂度: O ( n ) O(\sqrt{n}) O(n)
具体实现:
预处理
const int N = 1e5 +5, M = 500;
int arr[N], sq, n, len;
int st[M], ed[M], pos[N], tag[M];
vector<int> block[M]; //划分的块里的数字
划分块
void build(){
sq = sqrt(n); //sq:分的块数
len = n / sq; //寻常块的长度
for (int i = 1; i <= sq; i++){
st[i] = len * (i - 1) + 1; //第i块的开始下标
ed[i] = len * i; //第i块的结束下标
}
ed[sq] = n; //将最后剩余放入最后一块中
for (int i = 1; i <= sq; i++){
for (int j = st[i]; j <= ed[i]; j++){
pos[j] = i; //代表j属于第i块
block[i].push_back(arr[j]); //将该数字放入第i块中
}
}
for (int i = 1; i <= sq; i++){ //对块内数据排序,对于需要二分题需要
sort(block[i].begin(), block[i].end());
}
}
如果对于块内数据需要排序,可用到更新块内数据函数
void reset(int x)
{
block[x].clear();
for (int i = st[x]; i <= ed[x]; i++){
block[x].push_back(arr[i]);
}
sort(block[x].begin(), block[x].end());
}
为 ( l , r ) (l,r) (l,r)中的数据增加c
void add(int l, int r, int c){
if (pos[l] == pos[r]){ //数据在同一块内
for (int i = l; i <= r; i++){
arr[i] += c;
}
reset(pos[l]); //重新排列打乱顺序的块
}
else{
for (int i = l; i <= ed[pos[l]]; i++){ //左零散暴力
arr[i] += c;
}
reset(pos[l]);
for (int i = st[pos[r]]; i <= r; i++){ //右零散暴力
arr[i] += c;
}
reset(pos[r]);
for (int i = pos[l] + 1; i < pos[r]; i++){ //整块打上标记
tag[i] += c;
}
}
}
总体思想:将一个大区间划分成小区间,求区间问题时可以对区间进行总体操作,对两遍的零散数据进行暴力操作。