数列分块2(学习)
用途:区间加法,区间查找小于 的数的个数。
思路:对于区间加法依旧分块处理,由于是查找小于 的数的个数,显然要用到排序,所以我们先分块,然后对每一块进行排序。因为区间加法存在对非整块的处理,所以区间加后数的大小位置可能发生变化,所以要进行重新排序。对于区间查找也是分块思想,用二分查找实现即可。
时间复杂度:
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
#define mst(a) memset(a,0,sizeof a)
int n,bk,a[N],tag[N],bl[N];
vector<int>v[N];
void reset(int x){ //重新对块排序.
v[x].clear();
for(int i=(x-1)*bk+1;i<=min(x*bk,n);i++)
v[x].push_back(a[i]);
sort(v[x].begin(),v[x].end());
}
void add(int l,int r,int c){
for(int i=l;i<=min(bk*bl[l],r);i++) //左边不完整的块
a[i]+=c;
reset(bl[l]);//每次要重置。
if(bl[l]!=bl[r]){
for(int i=(bl[r]-1)*bk+1;i<=r;i++) //右边不完整的块
a[i]+=c;
reset(bl[r]);//重置.
}
for(int i=bl[l]+1;i<=bl[r]-1;i++)
tag[i]+=c;
}
int query(int l,int r,int c){
int ans=0;
for(int i=l;i<=min(bk*bl[l],r);i++) //查询也是分整块与非整块处理.
if(a[i]+tag[bl[l]]<c) ans++;
if(bl[l]!=bl[r]){
for(int i=(bl[r]-1)*bk+1;i<=r;i++)
if(a[i]+tag[bl[r]]<c) ans++;
}
for(int i=bl[l]+1;i<=bl[r]-1;i++){
int x=c-tag[i];
ans+=lower_bound(v[i].begin(),v[i].end(),x)-v[i].begin();
}
return ans;
}
int main(){
scanf("%d",&n),bk=sqrt(n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
bl[i]=(i-1)/bk+1;
v[bl[i]].push_back(a[i]);
}
for(int i=1;i<=bl[n];i++)
sort(v[i].begin(),v[i].end());
for(int i=1,op,l,r,c;i<=n;i++){
scanf("%d%d%d%d",&op,&l,&r,&c);
if(!op) add(l,r,c);
else printf("%d\n",query(l,r,c*c));
}
return 0;
}