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;
    }
    
  • 相关阅读:
    Mysql存储引擎
    数据库事务的四大特性以及事务的隔离级别
    万万没想到,面试中,连 ClassLoader类加载器 也能问出这么多问题
    万万没想到,JVM内存区域的面试题也可以问的这么难?
    SQL Server读取及导入Excel数据
    SQL Server加密与解密
    线程之间如何通信
    mybatis 批量更新 批量添加
    vue echarts 从后台获取数据形成饼图,柱状图,折线图
    vue 视频播放
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu2801.html
Copyright © 2011-2022 走看看