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

版权声明:若希望转载,在评论里直接说明即可,谢谢! https://blog.csdn.net/SSL_ZYC/article/details/82935631

题目大意:

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

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

思路:

30分做法:

暴力。

100分做法:

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


代码:

#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;
}

猜你喜欢

转载自blog.csdn.net/SSL_ZYC/article/details/82935631