BZOJ3343 教主的魔法(分块)
因为没有权限号所以挂洛谷链接
【题目分析】
发现很久没动过分块了,找几道题来做一做。。。。
分块好题,因为涉及区间加,那么开一个标记数组,类似于线段树的lazy标记。
询问区间大于等于k的数的个数,同一块内直接暴力统计,整块二分求解(二分永远最玄学啊。。。)
区间加,块内暴力加,整块就在标记数组上打标记即可。
【代码~】
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+10;
int n,q;
int h[MAXN],hh[MAXN];
int l[MAXN],r[MAXN],sig[MAXN];
int siz,num,belong[MAXN];
int Read(){
int i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
void px(int x){
int a=l[x],b=r[x];
for(int i=a;i<=b;++i)
hh[i]=h[i];
sort(hh+a,hh+b+1);
}
void fk(){
siz=sqrt(n);
num=(n/siz)+(n%siz?1:0);
for(int i=1;i<num;++i){
l[i]=(i-1)*siz+1;
r[i]=i*siz;
}
l[num]=r[num-1]+1;
r[num]=n;
for(int i=1;i<=n;++i)
belong[i]=((i-1)/siz)+1;
for(int i=1;i<=num;++i)
px(i);
}
int calc(int x,int y){
int a=l[x],b=r[x];
while(a<=b){
int mid=a+b>>1;
if(hh[mid]<y)
a=mid+1;
else
b=mid-1;
}
return r[x]-a+1;
}
void add(int x,int y,int k){
if(belong[x]==belong[y]){
if(sig[belong[x]]){
for(int i=l[belong[x]];i<=r[belong[x]];++i)
h[i]+=sig[belong[x]];
}
sig[belong[x]]=0;
for(int i=x;i<=y;++i)
h[i]+=k;
px(belong[x]);
return ;
}
for(int i=x;i<=r[belong[x]];++i)
h[i]+=k;
for(int i=l[belong[y]];i<=y;++i)
h[i]+=k;
px(belong[x]),px(belong[y]);
for(int i=belong[x]+1;i<belong[y];++i)
sig[i]+=k;
}
int sum(int x,int y,int k){
int ret=0;
if(belong[x]==belong[y]){
for(int i=x;i<=y;++i)
if(h[i]+sig[belong[x]]>=k)
ret++;
return ret;
}
for(int i=x;i<=r[belong[x]];++i)
if(h[i]+sig[belong[x]]>=k)
ret++;
for(int i=l[belong[y]];i<=y;++i)
if(h[i]+sig[belong[y]]>=k)
ret++;
for(int i=belong[x]+1;i<belong[y];++i)
ret+=calc(i,k-sig[i]);
return ret;
}
int main(){
n=Read(),q=Read();
for(int i=1;i<=n;++i)
h[i]=Read();
fk();
while(q--){
char cz[5];
scanf("%s",cz);
int l=Read(),r=Read(),k=Read();
if(cz[0]=='M')
add(l,r,k);
else
cout<<sum(l,r,k)<<'\n';
}
return 0;
}