zoukankan      html  css  js  c++  java
  • 【洛谷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('
    ');
        }
        return fwrite(Fout,1,FoutSize,stdout),0;
    }
    
  • 相关阅读:
    Android 编程下 Eclipse 恢复被删除的文件
    Android 编程下背景图片适配工具类
    Android 编程下 Managing Your App's Memory
    Android 编程下代码之(QQ消息列表滑动删除)
    Android 编程下 Canvas and Drawables
    Android 编程下 AlarmManager
    Android 编程下去除 ListView 上下边界蓝色或黄色阴影
    Java 编程下字符串的 16 位、32位 MD5 加密
    C#枚举类型和int类型相互转换
    MVC和普通三层架构的区别
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu2801.html
Copyright © 2011-2022 走看看