[题解] [笔记] 洛谷-P2357守墓人 & 分块学习笔记

[题解] [笔记] 洛谷-P2357守墓人 & 分块学习笔记

原题链

巨佬儿子的博客

分块算法

​ 分块算法其实是一种暴力算法,主要用于处理序列问题,一般而言是将原序列(长度为你])划分为大小n的块,但存在序列不能完全被刚好分完的情况,因此在序列的末尾可能存在大小不足n的散块.对于两边的散块,每次操作时暴力修改或查询,如果询问或操作区间包含了完整的块,则选择打标记,在询问时再下传标记.

一些规定

​ block[i]代表第i个元素属于第block[i]个块,size代表每个块的大小(不包括散块大小)

对于此题

​ 题目对于主墓地有特殊要求,因此我们每次也特殊处理,在修改操作时,第一次循环for(int i = l;i <= min(block[l] * size,r);i++)从左端点l开始,如果l,r在同一个块内则r < block[l] * size,block[l] * size代表l属于的块的右端点坐标,这个的证明可以从求block[i]的代码反推(block[i] = (i - 1) / size + 1).其余的就是一些细节了.然后就没了.

AC代码

#include <bits/stdc++.h>
using namespace std;
long long block[200010],tag[200010];
long long sum[200010],a[200010];
int main(){
	int n,f,size;
	scanf("%d%d",&n,&f);
	size = sqrt(n);
	for(int i = 1;i <= n;i++){
		scanf("%lld",&a[i]);
		block[i] = (i - 1) / size + 1;
		sum[block[i]] += a[i];
	}
	for(int tt = 1;tt <= f;tt++){
		int opt;
		scanf("%d",&opt);
		if(opt == 1){
			long long l,r,k;
			scanf("%lld%lld%lld",&l,&r,&k);
			for(int i = l;i <= min(r,block[l] * size);i++){
				a[i] += k;
				sum[block[i]] += k;
			}
			if(block[l] != block[r]){
				for(int i = r;i >= (block[r] - 1) * size + 1;i--){
					a[i] += k;
					sum[block[i]]  += k;
				}
			}
			for(int i = block[l] + 1;i <= block[r] - 1;i++){
				tag[i] += k;
			}
		}
		else if(opt == 2){
			int k;
			scanf("%d",&k);
			a[1] += k;
			sum[block[1]] += k;
		}
		else if(opt == 3){
			int k;
			scanf("%d",&k);
			a[1] -= k;
			sum[block[1]] -= k;
		}
		else if(opt == 4){
			long long l,r;
			long long ans = 0;
			scanf("%lld%lld",&l,&r);
			for(int i = l;i <= min(r,block[l] * size);i++){
				ans += a[i] + tag[block[i]];
			}
			if(block[l] != block[r]){
				for(int  i = r;i >= (block[r] - 1) * size + 1;i--){
					ans += a[i] + tag[block[i]];
				}
			}
			for(int i = block[l] + 1;i <= block[r] - 1;i++){
				ans += sum[i] + tag[i] * size;
			}
			printf("%lld\n",ans);
		}
		else if(opt == 5){
			printf("%lld\n",a[1] + tag[1]);
		}
	}
	return 0;
}

P.s.:

好博客

LOJ上有分块练习1~9

posted @ 2020-08-23 16:42  czyczy  阅读(165)  评论(0编辑  收藏  举报