题目:
分析:
先将数组分成sqrt(n)块,每块sqrt(n)个元素,区间修改时,利用线段树的lazy标记,如果待修改的区间包含了完整的块x,那么lazy[x] += c,剩余的区间暴力更新,为了方便查询,我们同时用另一个数组来维护每个块元素的有序性,因为有lazy标记,修改时我们只需要从新维护暴力修改的块的有序性,如果查询的区间包含了完整的块,那么可以在这个块中二分求出 >= c-lazy[x] 的元素个数,剩下的区间同样暴力查询
对于块x,左端点:(x-1) * sqrt(n) + 1,右端点:x * sqrt(n)
对于下标x,属于块(x-1)/sqrt(n) + 1
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6+15;
int a[MAXN],b[MAXN],pos[MAXN],lazy[MAXN],n,q,m,block;
void build(int x){
int l = (x-1)*block+1,r = min(x*block,n);
for(int i = l;i <= r; ++i) b[i] = a[i];
sort(b+l,b+r+1);
}
void Updata(int l,int r,int v){
if(pos[l] == pos[r]){
for(int i = l;i <= r; ++i) a[i] += v;
}
else{
for(int i = l;i <= pos[l]*block; ++i) a[i] += v;
for(int i = (pos[r]-1)*block+1;i <= r; ++i) a[i] += v;
for(int i = (pos[l]+1);i < pos[r]; ++i) lazy[i] += v;
}
build(pos[l]); //pos[l]块的部分元素改变了,从新维护该块的有序性
build(pos[r]);
}
int Query(int l,int r,int v){
int res = 0;
if(pos[l] == pos[r]){
for(int i = l;i <= r; ++i) if(a[i]+lazy[pos[l]] >= v) res++;
}
else{
for(int i = l;i <= pos[l]*block; ++i) if(a[i]+lazy[pos[i]] >= v) res++;
for(int i = (pos[r]-1)*block+1;i <= r; ++i) if(a[i]+lazy[pos[i]] >= v) res++;
for(int i = (pos[l]+1);i < pos[r]; ++i){
int l = (i-1)*block+1,r = min(i*block,n);
int tep = lower_bound(b+l,b+r+1,v-lazy[i]) - b;
res += r-tep+1;
}
}
return res;
}
int main(){
cin >> n >> q;
for(int i = 1;i <= n; ++i) cin >> a[i];
block = sqrt(n*1.0);
m = n / block + (n % block > 0); //总块数
for(int i = 1;i <= m; ++i) build(i);
for(int i = 1;i <= n; ++i) pos[i] = (i-1)/block + 1;
char c;
int L,R,w;
while(q--){
cin >> c >> L >> R >> w;
if(c == 'M') Updata(L,R,w);
else cout << Query(L,R,w) << '\n';
}
return 0;
}