故事贩卖机 2018-02-21
BZOJ_3343_教主的魔法_分块+二分查找
题意:教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列被编号为1、2、……、N。每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)询问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。
分析:暴力!
每一块用一个辅助数组来排序,询问时整块二分查找大于等于C的位置,零散的暴力重构。
代码:
#include <stdio.h> #include <string.h> #include <algorithm> #include <math.h> using namespace std; #define N 1000010 #define LL long long void read(LL &x){ int f=1;x=0;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();} x*=f; } int L[1010],R[1010],pos[N],t,is[1010],add[1010],n,q; LL a[N],b[N]; int find(int blo,LL x){ int l=L[blo],r=R[blo]+1; while(l<r){ int mid=l+r>>1; if(b[mid]<x)l=mid+1; else r=mid; }return R[blo]-l+1; } inline void pre(int blo){ for(int i=L[blo];i<=R[blo];i++)b[i]=a[i]; sort(b+L[blo],b+R[blo]+1); } void up(int l,int r,LL c){ int p=pos[l],q=pos[r]; if(p==q){ for(int i=l;i<=r;i++){ a[i]+=c; } pre(p); }else{ for(int i=p+1;i<q;i++){ add[i]+=c; } for(int i=l;i<=R[p];i++){ a[i]+=c; } for(int i=L[q];i<=r;i++){ a[i]+=c; } pre(p);pre(q); } } int query(int l,int r,LL c){ int p=pos[l],q=pos[r]; int ans=0; if(p==q){ for(int i=l;i<=r;i++)if(a[i]+add[p]>=c)ans++; }else{ for(int i=p+1;i<q;i++){ ans+=find(i,c-add[i]); } for(int i=l;i<=R[p];i++){ if(a[i]+add[p]>=c)ans++; } for(int i=L[q];i<=r;i++){ if(a[i]+add[q]>=c)ans++; } } return ans; } char op[10]; int main(){ scanf("%d%d",&n,&q); t=sqrt(n); for(int i=1;i<=t;i++){ L[i]=R[i-1]+1,R[i]=t*i; for(int j=L[i];j<=R[i];j++){ read(a[j]);pos[j]=i; } } if(n-t*t){ t++;L[t]=R[t-1]+1;R[t]=n; for(int i=L[t];i<=R[t];i++){ read(a[i]);pos[i]=t; } } for(int i=1;i<=t;i++)pre(i); int x,y,z; while(q--){ scanf("%s%d%d%d",op,&x,&y,&z); if(op[0]=='M'){ up(x,y,1ll*z); }else{ printf("%d\n",query(x,y,1ll*z)); } } }