传送门:LOJ#6278 数列分块入门2
题意:给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。
为了确保更快地找到区间内小于某个值x的元素个数,对序列进行排序,使得块内元素有序。每次操作完毕,只需对残缺块进行重排。因为对残缺块整体加,可能会破坏完整块的有序性;而对整个块加法,不会改变其有序性,所以没必要重排。询问过程完整块二分询问,残缺块暴力枚举。更详细的请参见代码1(预处理就是排序).
有趣的发现:还有一种不用排序的做法,效率似乎更高——预处理完整块的最值。
询问时,对残缺块,暴力枚举;
对完整块,<1>如果块的最大值小于x,那么整个块都小于x,ans+=block;
<2>如果块的最小值大于等于x,那么整个块都大于等于x,ans+=0,
<3>其他情况,对整个块进行暴力枚举。
更详细的请参见代码2(预处理就是求块的最值).
代码1和代码2运行情况对比:
代码1:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=50010;
const int maxm=sqrt(maxn)+10;
ll read(){ //读入挂
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
int n,block;
int a[maxn],pos[maxn],tag[maxn];
vector<int>v[maxm];
void reset(int x){
v[x].clear();
for(int i=(x-1)*block+1;i<=min(x*block,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(pos[l]*block,r);i++)
a[i]+=c;
reset(pos[l]);
if(pos[l]!=pos[r]){
for(int i=(pos[r]-1)*block+1;i<=r;i++)
a[i]+=c;
reset(pos[r]);
}
for(int i=pos[l]+1;i<=pos[r]-1;i++)
tag[i]+=c;
}
int query(int l,int r,int c){
int ans=0;
for(int i=l;i<=min(pos[l]*block,r);i++)
if(a[i]+tag[pos[l]]<c) ans++;
if(pos[l]!=pos[r]){
for(int i=(pos[r]-1)*block+1;i<=r;i++)
if(a[i]+tag[pos[r]]<c) ans++;
}
for(int i=pos[l]+1;i<=pos[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(){
n=read();
block=sqrt(n);
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++){
pos[i]=(i-1)/block+1;
v[pos[i]].push_back(a[i]);
}
//cout<<"**"<<endl;;
for(int i=1;i<=pos[n];i++){
sort(v[i].begin(),v[i].end());
}
int opt,l,r,c;
for(int i=1;i<=n;i++){
opt=read();
l=read();
r=read();
c=read();
if(opt==0) add(l,r,c);
else printf("%d\n",query(l,r,c*c));
}
return 0;
}
代码2:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
const int maxn=50010;
int n,block;
int a[maxn],pos[maxn],tag[maxn],mx[maxn],mn[maxn];
void add(int l,int r,int c){
for(int i=l;i<=min(pos[l]*block,r);i++){
a[i]+=c;
mx[pos[i]]=max(mx[pos[i]],a[i]+tag[pos[i]]);
mn[pos[i]]=min(mn[pos[i]],a[i]+tag[pos[i]]);
}
if(pos[l]!=pos[r]){
for(int i=(pos[r]-1)*block+1;i<=r;i++){
a[i]+=c;
mx[pos[i]]=max(mx[pos[i]],a[i]+tag[pos[i]]);
mn[pos[i]]=min(mn[pos[i]],a[i]+tag[pos[i]]);
}
}
for(int i=pos[l]+1;i<=pos[r]-1;i++){
tag[i]+=c;
mx[i]+=c;
mn[i]+=c;
}
}
int query(int l,int r,int c){
int ans=0;
for(int i=l;i<=min(pos[l]*block,r);i++)
if(a[i]+tag[pos[l]]<c) ans++;
if(pos[l]!=pos[r]){
for(int i=(pos[r]-1)*block+1;i<=r;i++)
if(a[i]+tag[pos[r]]<c) ans++;
}
for(int i=pos[l]+1;i<=pos[r]-1;i++){
if(mx[i]<c) ans+=block;
else if(mn[i]>=c) continue;
else{
for(int j=(i-1)*block+1;j<=i*block;j++){
if(a[j]+tag[i]<c) ans++;
}
}
}
return ans;
}
int main(){
scanf("%d",&n);
block=sqrt(n);
int num=n/block;
if(n%block) num++;
for(int i=1;i<=num;i++){
mx[i]=-INF;
mn[i]=INF;
}
for(int i=1;i<=n;i++){
pos[i]=(i-1)/block+1;
}
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
mx[pos[i]]=max(mx[pos[i]],a[i]);
mn[pos[i]]=min(mn[pos[i]],a[i]);
}
int opt,l,r,c;
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&opt,&l,&r,&c);
if(opt==0) add(l,r,c);
else printf("%d\n",query(l,r,c*c));
}
return 0;
}