【bzoj3343】 教主的魔法(分块入门)
题意:一个含n个数的区间(n<=1000000),k次操作,两种操作:
M a,b,c 将区间[a,b]中的每一个数加c;
A a,b,c 查询区间[a,b]中大于c的数的个数;
思路:将区间分为sqrt(n)块,对每块进行排序;
对于查询区间,两端的块外区间暴力处理,中间的块内区间特殊处理(二分);
借助add标记,实现优化;
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int add[5000100],num[5000100]; int pos[5000100],b[5000100]; int t,n,m,block,temp; char str[5050]; void reset(int x) //区间排序 { int l=(x-1)*block+1; int r=min(x*block,n); for(int i=l;i<=r;i++) { b[i]=num[i]; } sort(b+l,b+r+1); } int find(int x,int v) //块内二分查询 { int l=(x-1)*block+1; int r=min(x*block,n); int last=r; while(l<=r) { int mid=(l+r)/2; if(b[mid]<v) l=mid+1; else r=mid-1; } return last-l+1; } void update(int x,int y,int v) { if(pos[x]==pos[y]) { for(int i=x;i<=y;i++) num[i]+=v; } else { for(int i=x;i<=pos[x]*block;i++) num[i]+=v; for(int i=(pos[y]-1)*block+1;i<=y;i++) num[i]+=v; } reset(pos[x]);reset(pos[y]);//注意 for(int i=pos[x]+1;i<pos[y];i++) add[i]+=v; } int query(int x,int y,int v) { int sum=0; if(pos[x]==pos[y]) { for(int i=x;i<=y;i++) if(num[i]+add[pos[i]]>=v) sum++; } else { for(int i=x;i<=pos[x]*block;i++) if(num[i]+add[pos[i]]>=v) sum++; for(int i=(pos[y]-1)*block+1;i<=y;i++) if(num[i]+add[pos[i]]>=v) sum++; } for(int i=pos[x]+1;i<pos[y];i++) sum+=find(i,v-add[i]); return sum; } int main() { int i,j,k,x,y,z; while(scanf("%d%d",&n,&m)!=EOF) { block=(int)sqrt(n); for(i=1;i<=n;i++) { scanf("%d",&num[i]); pos[i]=(i-1)/block+1; b[i]=num[i];//不要忽略 } if(n%block) temp=n/block+1; else temp=n/block; for(i=1;i<=temp;i++) reset(i); for(i=1;i<=m;i++) { scanf("%s%d%d%d",str,&x,&y,&z); if(str[0]=='M') update(x,y,z); else printf("%d\n",query(x,y,z)); } } return 0; }
有些梦想现在不去实现,以后就再也没机会了!!