P2357 守墓人(分块)

传送门

算法分析

  • 区间修改 区间查询 单点修改 单点查询 显然可以用线段树做
  • 但是我们用另外一种神奇的暴力把这个 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;
}

posted @ 2020-09-04 21:29  HISKrrr  阅读(103)  评论(0编辑  收藏  举报