• 【洛谷P2801】教主的魔法【分块】【二分】


    题目大意:

    题目链接:https://www.luogu.org/problemnew/show/P2801
    给出一串数列,有两个操作:

    • M x y z:M\ x\ y\ z:将位置在xxyy之间的数全部加上zz
    • A x y z:A\ x\ y\ z:查询位置在xxyy之间有多少个数不小于zz

    思路:

    30分做法:

    暴力。

    100分做法:

    考虑用分块。
    对于每次修改操作,如果修改的位置都在一个区间,那么就暴力修改。
    如果不在同一个区间,那么就将中间的区间addadd数组全部加上zz,其余两边暴力。
    修改很简单吧。
    那么对于查询操作,如果查询的位置在同一个区间,暴力。
    如果不在同一个区间,可以想到,如果这个区间是单调递增的,那么就可以用二分查找,找到第一个比zz小的,那么后面的全部比zz大(或等于)。
    那么就在每次修改之后维护另一个数组a[i]a[i],保证每一个块在aa数组中都是单调递增的。就可以了。


    代码:

    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <cmath>
    #define N 1000100
    using namespace std;
    
    int tall[N],pos[N],a[N],L[N],R[N],add[N];
    int n,m,t;
    char c;
    
    void reset(int x)  //重构a数组
    {
        for (int i=L[x];i<=R[x];i++)
         a[i]=tall[i];
        sort(a+L[x],a+R[x]+1);
    }
    
    int sum(int l,int r,int z)  //暴力求个数
    {
        int ans=0;
        for (int i=l;i<=r;i++)
         if (tall[i]>=z) ans++;
        return ans;
    }
    
    int find(int l,int r,int z)  //二分求个数
    {
        int mid,p=r;
        while(l<=r)
        {
            mid=(l+r)/2;
            if(a[mid]<z) l=mid+1;
             else r=mid-1;
        }
        return p-l+1;
    }
    
    int ask(int l,int r,int z)
    {
        int q=pos[l],p=pos[r];
        if (q==p) return sum(l,r,z-add[q]);  //每次z要减去add[这个块],因为这个块整体增加了add[i],所以查找时要减去add[i]
        int ans=0;
        for (int i=q+1;i<p;i++)
         ans+=find(L[i],R[i],z-add[i]);
        return ans+sum(l,R[q],z-add[q])+sum(L[p],r,z-add[p]);
    }
    
    void change(int l,int r,int z)
    {
        int q=pos[l],p=pos[r];
        if (q==p)
        {
            for (int i=l;i<=r;i++) tall[i]+=z;
            reset(q);
            return;
        }
        for (int i=l;i<=R[q];i++) tall[i]+=z;
        for (int i=L[p];i<=r;i++) tall[i]+=z;
        reset(q);
        reset(p);  //重构
        for (int i=q+1;i<p;i++)
         add[i]+=z;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++) scanf("%d",&tall[i]);
        t=(int)sqrt(n);
        for (int i=1;i<=t;i++)
        {
            L[i]=R[i-1]+1;
            R[i]=i*t;
        }
        if (R[t]<n)
        {
            t++;
            L[t]=R[t-1]+1;
            R[t]=n;
        }
        for (int i=1;i<=t;i++)
        {
            for (int j=L[i];j<=R[i];j++)
          	 pos[j]=i;
            reset(i);
        } 
        int x,y,z;
        while (m--)
        {
            cin>>c;
            scanf("%d%d%d",&x,&y,&z);
            if (c=='A') printf("%d\n",ask(x,y,z));
             else change(x,y,z);
        }
        return 0;
    }
    
  • 相关阅读:
    plsql中查看sql执行计划
    数据库连接查询
    Class 的基本语法
    javascript中的描述对象(Descriptor)
    Nop源码分析三 周三 晴 天气不错
    NOP源码分析 二 周二
    NOP源码分析 一
    react-wow
    8、vue中得监听属性:watch--- /////watch、computed、methods得区别
    7.Vue_____keep-alive(结合路由)
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998548.html
Copyright © 2020-2023  润新知