11.06 感谢h学长在下午教我分块的基础,莫队的思想
这里大概讲一下我目前了解的分块,并记录一下菜鸡如何成长为菜鸟
以此题作为一个样例
题意:我们有n各阵营,每个阵营中有ai个人,接下来有多种操作
- add i,j 代表了第i个阵营增加了j个人
- sub i,j 代表了第i个阵营减少了j个人
- Query i,j询问 [i,j]区间内一共有多少人
用s数组存n个数的时候下标从1开始,以免后面计算时的特判
-
首先我们将n个数字分成sqtr(n)块那么每一块有blo = (int) sqrt( n )个数字,那么通过一个O(n)的预处理每一个数字的属于第(i-1)/blo+1块
for(int i=1;i<=n;i++) scanf("%d",&s[i]),pos[i]=(i-1)/blo+1;
-
我们访问每一块中的每一个元素,用一个 sum数组 记录每一块的总和,这样可以减少我们以后暴力的时间
for(int i=1;i<=pos[n];i++){ sum[i]=0; for(int j=(i-1)*blo+1;j<=min(n,i*blo);j++) sum[i]+=s[j]; }
-
接下来就是我们的修改或询问操作了
- 修改
- add 首先让第 i 个位置的数加上 j 大小并且这个数属于的块的总和也应该加上 j 的大小
- sub 调用add函数即可,只需把加 j 改为加 -j
- 询问区间 [i,j]
-
我们预处理将每一块的总和放进了sum数组,但是 i 和 j 区间内不一定都为完整的一块(完整块)所以我们先将答案 ans+=sum[i](完整块的总和)在暴力去加上其余的s[i]
-
那么完整块有哪些,i 的下一块到 j 的上一块不恰好是的完整块嘛,即[ pos[ i ] + 1, pos[ j ] - 1 ]
* for(int i=pos[l]+1;i<=pos[r]-1;i++) ans+=sum[i];
-
加下来就是 特判(pos[ i ]是否等于pos[ j ])加上 暴力 加上每一个在pos[ i ]块,pos[ j ]块中的每一个元素了
* if(pos[l]!=pos[r]){ for(int i=l;i<=pos[l]*blo;i++) ans+=s[i]; for(int i=(pos[r]-1)*blo+1;i<=r;i++) ans+=s[i]; } else for(int i=l;i<=r;i++) ans+=s[i];
-
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e4+7;
int n;
int pos[maxn],blo,sum[300];
int s[maxn];
char ch[10];
void add(int x,int val){
sum[pos[x]]+=val;
s[x]+=val;
}
int query(int l,int r){
int ans=0;
for(int i=pos[l]+1;i<=pos[r]-1;i++) ans+=sum[i];
if(pos[l]!=pos[r]){
for(int i=l;i<=pos[l]*blo;i++) ans+=s[i];
for(int i=(pos[r]-1)*blo+1;i<=r;i++) ans+=s[i];
}
else for(int i=l;i<=r;i++) ans+=s[i];
return ans;
}
int l,r,val;
int main (){
int t;scanf("%d",&t);
for(int cas=1;cas<=t;cas++){
scanf("%d",&n);blo=(int)sqrt(n*1.0);
for(int i=1;i<=n;i++) scanf("%d",&s[i]),pos[i]=(i-1)/blo+1;
for(int i=1;i<=pos[n];i++){
sum[i]=0;
for(int j=(i-1)*blo+1;j<=min(n,i*blo);j++) sum[i]+=s[j];
}
printf("Case %d:\n",cas);
while(scanf("%s",ch)){
if(ch[0]=='E') break;
if(ch[0]=='A') {
scanf("%d %d",&l,&r);
add(l,r);
}
else if(ch[0]=='S') {
scanf("%d %d",&l,&r);
add(l,-r);
}
else {
scanf("%d %d",&l,&r);
printf("%d\n",query(l,r));
}
}
}
return 0;
}