数列分块2(学习)

数列分块2(学习)

传送门

用途:区间加法,区间查找小于 c c 的数的个数。

思路:对于区间加法依旧分块处理,由于是查找小于 c c 的数的个数,显然要用到排序,所以我们先分块,然后对每一块进行排序。因为区间加法存在对非整块的处理,所以区间加后数的大小位置可能发生变化,所以要进行重新排序。对于区间查找也是分块思想,用二分查找实现即可。

时间复杂度: O ( n l o g n + n n l o g n ) O(nlogn+n\sqrt{n}log\sqrt{n})

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;
} 

猜你喜欢

转载自blog.csdn.net/weixin_45750972/article/details/106245534
今日推荐