[题解] [笔记] 洛谷-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