P13977题解
题目链接
这道题是典型的数列分块。你问我为什么不用线段树,是因为用线段树容易写炸,毕竟这道题让你求的东西非常复杂,而用分块就可以十分暴力的解决。我们可以先把每个块都用一个vector维护,这是为了以后用upper_bound求,同时我们为了可以在块中二分肯定要把每个块都排序一遍,这样就可以求出小于c的数。
处理散块时我们直接暴力就可以,处理整块就需要二分求出小于的值,然后累加即可。其实代码还是较好写的。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,a[N],len,L[N],R[N],tag[N],pos[N],cnt,op,l,r,x,ans;
vector<int>v[N];
void biuld(){len=sqrt(n);while(1){++cnt, L[cnt]=(cnt-1)*len+1, R[cnt]=cnt*len;if(R[cnt]>=n){R[cnt]=n;break;}}for(int i=1;i<=cnt;i++){for(int j=L[i];j<=R[i];j++){pos[j]=i, v[i].emplace_back(a[j]); }sort(v[i].begin(),v[i].end());}
}
void change(int x){v[x].clear();for(int i=L[x];i<=R[x];i++) v[x].emplace_back(a[i]);sort(v[x].begin(),v[x].end());
}
void update(int l, int r, int x){if(pos[l]==pos[r]){for(int i=l;i<=r;i++) a[i]+=x;change(pos[l]);return;}for(int i=l;i<=R[pos[l]];i++) a[i]+=x;for(int i=pos[l]+1;i<pos[r];i++) tag[i]+=x;for(int i=L[pos[r]];i<=r;i++) a[i]+=x;change(pos[l]), change(pos[r]);
}
int find(int l, int r, int x){ans=0, x=x*x;if(pos[l]==pos[r]){for(int i=l;i<=r;i++){if(a[i]+tag[pos[l]]<x) ans++;}return ans;}for(int i=l;i<=R[pos[l]];i++) if(a[i]+tag[pos[l]]<x) ans++;for(int i=pos[l]+1;i<pos[r];i++){ans+=upper_bound(v[i].begin(),v[i].end(),x-tag[i]-1)-v[i].begin();}for(int i=L[pos[r]];i<=r;i++) if(a[i]+tag[pos[r]]<x) ans++;return ans;
}
signed main(){cin>>n;for(int i=1;i<=n;i++) cin>>a[i];biuld();for(int i=1;i<=n;i++){scanf("%lld %lld %lld %lld",&op,&l,&r,&x);if(!op) update(l,r,x);else cout<<find(l,r,x)<<'\n';}return 0;
}