分块小记
优雅的暴力
前言
如果对于一个序列,如果要求和。一个个地处理需要 \(O(n)\) 但是假如我们将序列预处理好,再算就是 \(O(1)\) 的。但要是还要修改呢?
分块就是一个折中的办法.可以做一些线段树没法做的事情.
区间加区间和
下面讲解区间加区间和的思路.
假设块长度为 \(len\),数量为 \(num=n/len\)。那么我们可以记录数组 \(tag_k\)。表示第 \(k\) 个块的标记,还有 \(s_k\) 表示第 \(k\) 个块的和。
如果现在要给 \([l,r]\) 都加上 \(p\).
如果区间覆盖了整个块,直接\(tag_x \leftarrow tag_x + p,s_x \leftarrow tag_x \times len\) (这个 \(len\) 可能受到边界的影响) 就可以了。
如果不是,直接在原数列上进行修改,即暴力。
查询的时候也是一样的.
如果区间覆盖了整个块,直接 \(ans\leftarrow ans+s_x\).
如果不是,直接暴力求和.
代码(luogu P3372)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5+10;
int a[N],id[N];
int len;
int s[N], tag[N];
#define l(i) ((i)*(len-1)+1)
#define r(i) (min((i)*len,n))
void bupd(int l,int r,int c) {
for(int i=l;i<=r;i++) {
a[i]+=c,s[id[i]]+=c;
}
}
void upd(int l,int r,int c) {
if(id[l] == id[r]) {
bupd(l,r,c);
return ;
}
bupd(l,r(id[l])),bupd(l(id[r]),r);
for(int i=id[l]+1;i<id[r];i++) {
tag[i] += c, s[i] += len*c;
}
}
void bqry(int l,int r,int mod) {
int p = 0;
for(int i=l;i<=r;i++) {
(p += a[i] + tag[id[i]]) %= mod;
}
return p;
}
int query(int l,int r,int mod) {
int ans = 0;
if(id[l] == id[r]) {
return bqry(l,r,mod);
}
(ans += bqry(l,r[id[l]],mod) + bqry(l(id[r]),r,mod))%=mod;
for(int i=id[l]+1;i<id[r];i++) {
(ans+=s[i])%=mod;
}
return ans%mod;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n; cin>>n;
len = sqrt(n);
for(int i=1;i<=n;i++) {
cin>>a[i];
id[i] = (i-1)/len + 1;
s[id[i]] += a[i];
}
while(n--) {
int o,l,r,k;
cin>>o>>l>>r>>k;
if(o==0) upd(l,r,k);
else cout<<query(l,r,k+1)<<"\n";
}
return 0;
}

浙公网安备 33010602011771号