P13977

主要讲一下代码细节,对于数据结构的原理请去看模板。

Solution

首先有两种操作,区间查询和区间加法,先说说加法。

首先判断 $l,r$ 是不是在一个块里,如果是就直接处理。
否则先把两端多出来的部分处理掉,让 $l,r$ 刚好到达块的端点,然后把中间的整块的懒标记加上 $c$ 就好了。

接下来是区间查询有多少个小于等于 $c^2$ 的个数,显然因为每次的查询不一样,是没有办法一直在线维护的。
所以这个数据给的很极限,我们可以在修改之后还排序,这样查询就很方便了。
所以还是一样先处理两边,因为加法之后会影响顺序,所以需要重新排序,然后查询的时候直接二分一下位置就可以了。

具体实现细节请看代码,个人认为应该容易看懂。

Code

#include<bits/stdc++.h>
using namespace std;
using LL=long long;
LL n,a[500005],lazy[500005],len,L[500005];
vector<LL> v[50005];
inline LL get(LL x){
	return x/len+1;
}
void resort(LL x){
	int i=L[x];v[x].clear();
	while(get(i)==x&&i<=n) v[x].push_back(a[i++]);
	sort(v[x].begin(),v[x].end());
}
void update(LL l,LL r,LL c){
	if(get(l)==get(r)){
		for(int i=l;i<=r;i++) a[i]+=c;
		resort(get(l));
	}else {
		int i=l,j=r;
		while (get(i) == get(l)) a[i++] += c;
		while (get(j) == get(r)) a[j--] += c;
		resort(get(l));resort(get(r));
		for (int k = get(i);k <= get(j);k++) lazy[k] += c;
	}
}
LL query(LL l,LL r,LL c){
	LL res=0;
	if(get(l)==get(r)){
		for(int i=l;i<=r;i++){
			if(a[i]+lazy[get(i)]<c) res++;
		}
	}
	else{
		int i=l,j=r;
		while(get(i)==get(l)) if(a[i++]+lazy[get(l)]<c) res++;
		while(get(j)==get(r)) if(a[j--]+lazy[get(r)]<c) res++;
		for (int k = get(i);k <= get(j);k++) {
			res += lower_bound(v[k].begin(), v[k].end(), c - lazy[k]) - v[k].begin();
		}
	}
	return res;
}
int main(){
	cin>>n;
	len=int(sqrt(n));
	memset(L,0x3f,sizeof(L));
	for(LL i=1;i<=n;i++){
		cin>>a[i];
		LL x=get(i);
		L[x]=min(L[x],i);
		v[x].push_back(a[i]); 
	}
	for(int i=1;i<=n/len;i++) sort(v[i].begin(),v[i].end());
	for(int i=1;i<=n;i++){
		LL l,r,c,op;
		cin>>op>>l>>r>>c;
		if(op) cout<<query(l,r,c*c)<<endl;
		else{
			update(l,r,c);
		}
	}
	return 0;
}