洛谷P2801
题目:
教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1、2、……、N。
每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)
CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。
WD巨懒,于是他把这个回答的任务交给了你。
分析:
增加身高——打标记
快速查找——排序(快排),二分
注意:
分块:
——块数,平方根复杂化
——边界,min(k*block,n)
二分:
边界,最后的取值
#include<bits/stdc++.h>
#define maxn 1000010
using namespace std;
int n,m,block,num;
int a[maxn],b[maxn],ls[maxn],rs[maxn],pos[maxn],s[maxn];
void reset(int k){
int l=(k-1)*block+1,r=min(k*block,n);
for(int i=l;i<=r;i++) b[i]=a[i];
sort(b+l,b+r+1);
ls[k]=l;rs[k]=r;
return ;
}
void work(int x,int y,int z){
int ans=0;
if(pos[x]==pos[y]){
for(int i=x;i<=y;i++)
if(a[i]+s[pos[x]]>=z)ans++;
}
else{
for(int i=x;i<=rs[pos[x]];i++)if(a[i]+s[pos[x]]>=z)ans++;
for(int i=ls[pos[y]];i<=y;i++)if(a[i]+s[pos[x]]>=z)ans++;
for(int i=pos[x]+1;i<=pos[y]-1;i++){
int left=ls[i],right=rs[i],mid;
while(left<=right){
mid=(left+right)/2;
if(b[mid]<z-s[i])left=mid+1;
else right=mid-1;
}
ans=ans+rs[i]-left+1;
}
}
printf("%d\n",ans);
return ;
}
void work1(int x,int y,int z){
if(pos[x]==pos[y])
for(int i=x;i<=y;i++)a[i]=a[i]+z;
else{
for(int i=x;i<=rs[pos[x]];i++)a[i]=a[i]+z;
for(int i=ls[pos[y]];i<=y;i++)a[i]=a[i]+z;
}
reset(pos[x]);reset(pos[y]);
for(int i=pos[x]+1;i<=pos[y]-1;i++)s[i]=s[i]+z;
return ;
}
int main(){
memset(s,0,sizeof(s));
scanf("%d %d\n",&n,&m);
block=sqrt(n*1.0);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
pos[i]=(i-1)/block+1;
b[i]=a[i];
}
num=n/block;if(n%num!=0)num++;
for(int i=1;i<=num;i++)reset(i);
for(int i=1;i<=m;i++){
int x,y,z;
char h;
scanf("\n%c %d %d %d",&h,&x,&y,&z);
if(h=='A')work(x,y,z);
if(h=='M')work1(x,y,z);
}
return 0;
}