把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷2801】教主的魔法(分块经典入门题)

点此看题面

大致题意: 给你一个序列,要你支持两种操作:第一种是区间加法,第二种是查询区间内大于等于\(x\)的数的个数。

考虑分块

这应该是一道比较经典的分块入门题吧。

首先,我们将序列分块。

对于修改操作,暴力修改两边的不完整的块,中间的块直接打标记记录即可。

对于询问操作,暴力求出两边的不完整的块的答案,中间的块我们可以在块内二分,最后将全部答案加起来即可。

代码

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define abs(x) ((x)<0?-(x):(x))
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch))
#define N 1000000
#define SIZE 1000
int FoutSize=0,OutputTop=0;char Fin[Fsize],*FinNow=Fin,*FinEnd=Fin,Fout[Fsize],OutputStack[Fsize];
using namespace std;
int n,Q,blo,MaxPos,a[N+5],pos[N+5],Add[N+5];
vector<int> v[SIZE+5];//用vector存下每个块中元素排序后的结果
inline void read(int &x)
{
    x=0;static char ch;
    while(!isdigit(ch=tc()));
    while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
}
inline void read_alpha(char &x)
{
    while(!isalpha(x=tc()));
} 
inline void write(int x)
{
    if(!x) return (void)pc('0');
    while(x) OutputStack[++OutputTop]=x%10+48,x/=10;
    while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;
}
inline void F5(int x)//更新一个块的vector
{
    register int i,lim=min(blo*x,n);
    for(v[x].clear(),i=blo*(x-1)+1;i<=lim;++i) v[x].push_back(a[i]);//将元素加入vecter中
    sort(v[x].begin(),v[x].end());//排序
}
inline void Update(int l,int r,int x)//区间加法
{
    register int i,lim;
    for(i=l,lim=min(blo*pos[l],r);i<=lim;++i) a[i]+=x;F5(pos[l]);//暴力修改左边不完整的块,并更新这个块的vector
    if(pos[l]^pos[r]) {for(i=r,lim=blo*(pos[r]-1)+1;i>=lim;--i) a[i]+=x;F5(pos[r]);}//如果左边与右边不在同一个块,就暴力修改右边不完整的块,并更新这个块的vector 
    for(i=pos[l]+1;i<pos[r];++i) Add[i]+=x;//中间的完整的块直接打标记即可
}
inline int find(int s,int x)//二分
{
    register int l=0,r=v[s].size()-1,mid,res=r+1,lim=r+1;
    while(l<=r)
    {
        if(v[s][mid=l+r>>1]>=x) r=(res=mid)-1;
        else l=mid+1;
    }
    return lim-res;//返回有编号为s的块内有多少个元素大于等于x
}
inline int Query(int l,int r,int x)//查询区间内大于等于x的数的个数
{
    register int i,lim,res=0;
    for(i=l,lim=min(blo*pos[l],r);i<=lim;++i) if(a[i]+Add[pos[l]]>=x) ++res;//暴力求解左边不完整的块
    if(pos[l]^pos[r]) for(i=r,lim=blo*(pos[r]-1)+1;i>=lim;--i) if(a[i]+Add[pos[r]]>=x) ++res;//如果左边与右边不在同一个块,就暴力求解右边不完整的块
    for(i=pos[l]+1;i<pos[r];++i) res+=find(i,x-Add[i]);//对于中间完整的块,二分求出答案,然后将答案加在一起
    return res;//返回答案
}
int main()
{
    register int i,j,l,r,x;register char op;
    for(read(n),read(Q),MaxPos=(n-1)/(blo=sqrt(n))+1,i=1;i<=n;++i) read(a[i]),pos[i]=(i-1)/blo+1;
    for(i=1;i<=MaxPos;++i) F5(i);//先预处理出每个块的vector
    while(Q--)
    {
        read_alpha(op),read(l),read(r),read(x);
        if(op^'A') Update(l,r,x);
        else write(Query(l,r,x)),pc('\n');
    }
    return fwrite(Fout,1,FoutSize,stdout),0;
}
posted @ 2018-10-28 21:19  TheLostWeak  阅读(215)  评论(0编辑  收藏  举报