传送门
算法分析
- 区间修改 区间查询 单点修改 单点查询 显然可以用线段树做
- 但是我们用另外一种神奇的暴力把这个 2e5的题碾过就会很舒服
- 分块是一种比较常见的思想 分块也就是分治 把一个大区间分成几个小区间计算
- 我们可以把每\(log_n\) 个数划分成一组 如果区间修改了整个块的话 不用去挨个修改单点的值 而是直接改掉整个块的信息 然后用lazy记录一下修改操作
- 如果并没有修改掉整个块 那么我们就可以暴力处理 因为如果没有覆盖整个块 则操作元素个数肯定小于\(2*log_n\) 那么暴力处理也不会有太大的时间损耗
- 如果查询的时候 查询区间跨过了整个块 那么我们直接统计块的信息就可以了 如果并没有跨过整个块 那么我们也一样直接暴力处理 注意先把每个块的lazy先处理掉 复杂度一样不会太高
- 然后预处理一下每个点所属于的块就好了 注意修改和查询时的lazy的修改以及一些细节 比如修改的区间左端点和右端点同属于一个块
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
typedef long long ll;
int a[maxn];
ll sum[maxn];
int lazy[maxn];
int bel[maxn];
int size;
void change(int l,int r,int k){//区间修改
if(bel[l] == bel[r]){ //暴力处理 l 和 r 在同一个块的情况
for(int i = l;i <= r;++i) a[i] += k,sum[bel[l]] += k;
return;
}
for(int i = l;i <= bel[l] * size;++i) a[i] += k,sum[bel[l]] += k; // 暴力处理左边
for(int i = r;i >= (bel[r] - 1) * size + 1;--i) a[i] += k,sum[bel[r]] += k; // 暴力处理右边
for(int i = bel[l] + 1;i <= bel[r] - 1;++i) lazy[i] += k,sum[i] += size * k; // 处理中间的块(这些块一定是整个覆盖掉的)
}
void ask(int l,int r){
ll ans = 0;
if(lazy[bel[l]]) {//把左端点的块的lazy下放
for(int i = (bel[l] - 1) * size + 1;i <= bel[l] * size;++i) a[i] += lazy[bel[l]];
lazy[bel[l]] = 0;
}
if(lazy[bel[r]]) {//右端点的块的lazy下放
for(int i = bel[r] * size;i >= (bel[r] - 1) * size + 1;--i) a[i] += lazy[bel[r]];
lazy[bel[r]] = 0;
}
//中间块的lazy没必要处理 因为我们只需要统计区间和 而lazy对块的sum是没有影响的
if(bel[l] == bel[r]) {//暴力处理左右端点属于同一个块的情况
for(int i = l;i <= r;++i) ans += a[i];
printf("%lld\n",ans);
return;
}
for(int i = l;i <= bel[l] * size;++i) ans += a[i];//暴力处理左端点所属的块
for(int i = r;i >= (bel[r] - 1) * size + 1;--i) ans += a[i];//暴力处理右端点所属于的块
for(int i = bel[l] + 1;i <= bel[r] - 1;++i) ans += sum[i]; // 处理中间块
printf("%lld\n",ans);
}
int main(){
int n,m;scanf("%d%d",&n,&m);
size = sqrt(n);
for(int i = 1;i <= n;++i){
bel[i] = (i - 1) / size + 1;
scanf("%d",&a[i]);
sum[bel[i]] += a[i];
}
for(int i = 1;i <= m;++i){
int flag;scanf("%d",&flag);
if(flag == 1) {int l,r,k; scanf("%d%d%d",&l,&r,&k); change(l,r,k);}
if(flag == 2) {int k;scanf("%d",&k); a[1] += k; sum[1] += k;}
if(flag == 3) {int k;scanf("%d",&k); a[1] -= k; sum[1] -= k;}
if(flag == 4) {int l,r; scanf("%d%d",&l,&r); ask(l,r);}
if(flag == 5) printf("%d\n",a[1]);
}
return 0;
}