分块
分块复杂度略微证明:
小花的题解qwq
分块相对其他数据结构优点:通用,码量较小,直观,不易出错,兼容性强
分块实现的基本框架:
划分块,预处理,操作或查询。
操作或查询通常为4步:
1.判断要操作或是查询的区间是否在一个块内
2.若在一个块内,暴力操作或查询
3.若不在一个块内,将除了最左边和最右边这两个块外其余的块进行整体的操作,即直接对块打上修改标记之类的
4.单独暴力处理最左边的块和最右边的块
同时分块的块内还可以使用别的数据结构或是操作以实现要求或进一步优化复杂度
把数列分成长度若干长度不超过\(\lfloor\sqrt n\rfloor\)的段,其中第\(i\)段的左端点为\((i-1)\lfloor\sqrt n\rfloor+1\),右端点为\(min(i\lfloor\sqrt n\rfloor,n)\)
预处理数组\(sum\),其中\(sum[i]\)表示\(i\)段区间和,设\(add[i]\)表示第\(i\)段的增量标记,起初\(add[i]=0\)
对于指令区间加操作
1.若\(l\)和\(r\)同时位于第\(i\)段内,把\(A[l]...A[r]\)全部\(+d\),令\(sum[i]+=d*(r-l+1)\)
2.若\(l\)处于第\(p\)段\(r\)处于第\(q\)段.
(1).对于\(i\in[p+1,q-1],令add[i]+=d\) (2).对于开头,结尾不足一整段的两部分,按照与第一种情况相同的方法朴素的更新
对于区间和的操作
1.若\(l与r\)同时位于\(i\)段内,则\((A[l]+A[l+1]+...+A[r])+(r-l+1)*add[i]\)就是答案
2.若\(l\)处于第\(p\)段,\(r\)处于第\(q\)段, \(ans+=sum[i]+add[i]*len[i],len[i]为第i段长度\)
(2)对于开头结尾不足一整段,暴力维护
\(复杂度O((N+Q)*\sqrt N)\),核心思想:大段维护,小段暴力
libre oj分块入门1
#include<cstdio>
#include<cmath>
#include<iostream>
#define maxn 50005
using namespace std;
int a[maxn],add[250],pos[maxn],block;
//a原数组,pos每个位置属于哪段
void adde(int x,int y,int z){
for (int i = x; i <= min(pos[x] * block, y); i++)//处理左端点所在的不完整的块
a[i] += z;
if (pos[x] != pos[y])//如果左右端点不在一个块里处理右端点所在的不完整的块
for (int i = (pos[y] - 1) * block + 1; i <= y; i++)
a[i] += z;
for (int i = pos[x] + 1; i <= pos[y] - 1; i++)//处理中间完整的区间
add[i] += z;
}
int main(){
int n,opt,c,l,r;
scanf("%d",&n);
block = sqrt(n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
pos[i] = (i - 1) / block + 1;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%d",&opt,&l,&r,&c);
if(!opt) adde(l,r,c);
else printf("%d\n",a[r]+add[pos[r]]);
}
}
分块入门2
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n, block;
const int MAXN = 5e4 + 10;
const int block_ = 250;
int w[MAXN], b[MAXN], lazy[block_];
vector<int> v[block_];
void spread(int x) //将不完整的块压入vector重新排序
{
v[x].clear();
int l = (x - 1) * block + 1, r = min(x * block, n);
for (int i = l; i <= r; i++) v[x].push_back(w[i]);
sort(v[x].begin(), v[x].end());
}
void add(int x, int y, int z) {
for (int i = x; i <= min(b[x] * block, y); i++) w[i] += z;
if (b[x] != b[y]) {
for (int i = (b[y] - 1) * block + 1; i <= y; i++) w[i] += z;
spread(b[y]);
}
for (int i = b[x] + 1; i <= b[y] - 1; i++) lazy[i] += z;
spread(b[x]);
}
ll ans(int x, int y, int z) {
int sum = 0;
int big = z * z;
for (int i = x; i <= min(b[x] * block, y); i++)
if (w[i] + lazy[b[i]] < big)
sum++;
if (b[x] != b[y])
for (int i = (b[y] - 1) * block + 1; i <= y; i++)
if (w[i] + lazy[b[i]] < big)
sum++;
for (int i = b[x] + 1; i <= b[y] - 1; i++)
sum += lower_bound(v[i].begin(), v[i].end(), big - lazy[i]) -
v[i].begin(); // STL大法好,自带二分查找,查找第一个大于等于查找元素的下标(vector从0开始)
return sum;
}
int main() {
ios::sync_with_stdio(false);
cin >> n;
block = sqrt(n);
for (int i = 1; i <= n; i++) {
cin >> w[i];
b[i] = (i - 1) / block + 1;
v[b[i]].push_back(w[i]);
}
for (int i = 1; i <= b[n]; i++) sort(v[i].begin(), v[i].end());
for (int i = 1; i <= n; i++) {
int opt, x, y, z;
cin >> opt >> x >> y >> z;
if (!opt)
add(x, y, z);
else
cout << ans(x, y, z) << endl;
}
return 0;
}
分块入门4/线段树板子
ll n,Q,len,a[maxn],num,pos[maxn];
#define h 320
ll tag[h],sum[h],ri[h],le[h];
void build(){
num = n /len; if(n % len) ++num;//共num块
for(int i = 1;i <= n;++i) a[i] = read(),pos[i] = (i-1)/len + 1;//注意减1
for(int i = 1;i <= num;++i)
le[i] = (i-1)*len + 1,ri[i] = i*len;
ri[num] = n;
for(int i = 1;i <= num;++i){
for(int j = le[i];j <= ri[i];++j)
sum[i] += a[j];
}
}
inline void adde(int l,int r,int c){
if(pos[l] == pos[r]){
for(int i = l;i <= r;++i) a[i] += c;
sum[pos[l]] += (r - l + 1) * c; return;
}
for(int i = l;i <= ri[pos[l]];++i)
a[i] += c;
sum[pos[l]] += (ri[pos[l]] - l + 1) * c;
for(int i = pos[l]+1;i < pos[r];++i) tag[i] += c;
for(int i = le[pos[r]];i <= r;++i)
a[i] += c;
sum[pos[r]] += (r - le[pos[r]] + 1) * c;
}
ll query(int l,int r){
ll ans = 0;
if(pos[l] == pos[r]){
for(int i = l;i <= r;++i) ans += a[i] + tag[pos[i]];
return ans;
}
for(int i = l;i <= ri[pos[l]];++i)
ans += a[i] + tag[pos[l]];
for(int i = pos[l]+1;i < pos[r];++i)
ans += tag[i] * len + sum[i];
for(int i = le[pos[r]];i <= r;++i)
ans += a[i] + tag[pos[r]];
return ans;
}
int main(){
cin >> n >> Q; len = 3*sqrt(n)/2; build();
ll op,l,r,c;
while(Q--){
op = read(); l = read(); r = read();
if(op & 1) c = read(),adde(l,r,c);
else printf("%lld\n",query(l,r));
}
}
P2574xor艺术
int n,m,a[maxn],len,pos[maxn]; char s[maxn];
int sum[450],tag[450],num,l[450],r[450];
void build(){
num = n / len; if(n % len) ++num;
for(int i = 0;i < n;++i)
a[i+1] = (s[i] == '1'),pos[i+1] = i/len + 1;
for(int i = 1;i <= num;++i)
l[i] = (i-1) * len + 1,r[i] = i*len;
r[num] = n;
for(int i = 1;i <= num;++i)
for(int j = l[i];j <= r[i];++j)
if(a[j]) ++sum[i];
}
void change(int le,int ri){
if(pos[le] == pos[ri]){//暴力维护小区间
for(int i = le;i <= ri;++i){
sum[pos[i]] += (a[i] == 1) ? -1 : 1;
a[i] ^= 1;
} return;
}
for(int i = le;i <= r[pos[le]];++i){//左碎块暴力维护
sum[pos[i]] += (a[i] == 1) ? -1 : 1;
a[i] ^= 1;
}
for(int i = pos[le]+1;i < pos[ri];++i)//大段打tag
tag[i] ^= 1;
for(int i = l[pos[ri]];i <= ri;++i){//右碎块暴力维护
sum[pos[i]] += (a[i] == 1) ? -1 : 1;
a[i] ^= 1;
}
}
int query(int le,int ri){
int ans = 0;
if(pos[le] == pos[ri]){//暴力统计小区间
for(int i = le;i <= ri;++i)
if(a[i] ^ tag[pos[i]]) ++ans;//a[i]=1,tag=0 || a[i]=0,tag=1有贡献
return ans;
}
for(int i = le;i <= r[pos[le]];++i)//左碎块暴力统计
if(a[i] ^ tag[pos[i]]) ++ans;
for(int i = pos[le]+1;i < pos[ri];++i)//大段直接用sum
ans += (tag[i]) ? (len - sum[i]) : sum[i];
for(int i = l[pos[ri]];i <= ri;++i)//右碎块暴力统计
if(a[i] ^ tag[pos[i]]) ++ans;
return ans;
}
int main(){
cin >> n >> m; cin >> s; len = sqrt(n); int op,ri,le;
build();
while(m--){
op = read(); le = read(); ri = read();
if(op) printf("%d\n",query(le,ri));
else change(le,ri);
}
}