洛谷 P2801 教主的魔法
话说分块的坑点好多啊,一不小心就会越界什么的真是麻烦QWQ
0x00 思路
面对区间修改区间查询,我们有两种基本思路:线段树和分块
可是很明显我们查询的东西不满足区间加法的性质,而且每次查询的标准在变,我们考虑直接上分块暴力维护
0x01 分块思路
定义块大小为\(size = sqrt(n)\)
查询/修改的区间为\([l,r]\),查询/修改的值为\(v\)
\(l\)所在的块为\(lb\),\(r\)所在的块为\(rb\)
查询:
-
对于完整的块,我们将其中的元素排序,二分查找最小的符合条件的数的位置,作差即可计算贡献
-
对于不完整的块,我们直接枚举每一个数,计算贡献
修改:
-
对于完整的块,我们用\(block\)数组打标记,表示整个块全部加上\(v\),就像线段树的懒标记一样,查找的时候刚好可以用上
-
对于不完整的块,我们直接枚举每个数,加上\(v\),然后重新将块内的元素排序
细节:
-
我们开两个数组,\(a\)和\(tmp\),\(a\)数组存储的是原数组,\(tmp\)存储的是排序后的数组,排序我们是每次只排序一块内的数
-
每次要重排序块内的数时,可以从\(a\)中复制一份到\(tmp\)中,然后排序
-
各种下标要注意,时刻保证块的右端点不能超过\(n\)
-
对于\(lb = rb\)的情况,要特判
0x02 Code
#include<bits/stdc++.h>
using namespace std;
#define N 1000010
#define M 1010
int read(){
int x=0; char c=getchar(); int flag=1;
while(!isdigit(c)) { if(c=='-') flag=-1; c=getchar(); }
while(isdigit(c)) { x=((x+(x<<2))<<1)+(c^48); c=getchar(); }
return x*flag;
}
int n,m,size,belong[N];
long long a[N],tmp[N],block[M];
signed main(){
n = read(),m = read(),size = sqrt(n);
for(int i = 1;i <= n;i ++) a[i] = read(),belong[i] = (i - 1) / size + 1,tmp[i] = a[i];
for(int i = 1;(i - 1) * size + 1 <= n;i ++){
int l = (i - 1) * size + 1,r = min(n,i * size);
sort(tmp + l,tmp + r + 1);
}
while(m --){
char opt;
int l,r,v;
scanf(" %c %d %d %d",&opt,&l,&r,&v);
if(opt == 'A'){
int lb = belong[l],rb = belong[r],ans = 0;
if(lb == rb){
for(int i = l;i <= r;i ++) if(a[i] + block[lb] >= v) ++ ans;
printf("%d\n",ans);
continue;
}
for(int i = lb + 1;i <= rb - 1;i ++){
int l = (i - 1) * size + 1,r = i * size,ps = r + 1;
while(l <= r){
int mid = ((l + r) >> 1);
if(tmp[mid] + block[i] >= v) { ps = mid; r = mid - 1; }
else l = mid + 1;
}
ans += i * size - ps + 1;
}
for(int i = l;i <= lb * size;i ++) if(a[i] + block[lb] >= v) ++ ans;
for(int i = (rb - 1) * size + 1;i <= r;i ++) if(a[i] + block[rb] >= v) ++ ans;
printf("%d\n",ans);
}
if(opt == 'M'){
int lb = belong[l],rb = belong[r];
if(lb == rb){
for(int i = l;i <= r;i ++) a[i] += v;
for(int i = (lb - 1) * size + 1;i <= min(lb * size,n);i ++) tmp[i] = a[i];
sort(tmp + (lb - 1) * size + 1,tmp + min(lb * size,n) + 1);
continue;
}
for (int i = lb + 1;i <= rb - 1;i ++) block[i] += v;
for (int i = l;i <= lb * size;i ++) a[i] += v;
for (int i = (lb - 1) * size + 1;i <= lb * size;i ++) tmp[i] = a[i];
sort(tmp + (lb - 1) * size + 1,tmp + lb * size + 1);
for (int i = (rb - 1) * size + 1;i <= r;i ++) a[i] += v;
for (int i = (rb - 1) * size + 1;i <= min(rb * size,n);i ++) tmp[i] = a[i];
sort(tmp + (rb - 1) * size + 1,tmp + min(rb * size,n) + 1);
}
}
return 0;
}