bzoj3343: 教主的魔法

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=3809

题解

  这样搞,我们把序列分块,每一块有个标记表示这个块被整体加了几。
  每次查询,对于完整的块,我们提前把里面的元素排好序,每次只要二分一下就好了。
  修改,完整的块,直接打标记即可;对于前后那些零散的,暴力修改之后,把这个块重新拍下序。

代码

//分块
#include <cstdio>
#include <algorithm>
#define maxn 1100000
#define maxs 2048
using namespace std;
int N, Q, a[maxn], tag[maxs], tmp[maxn], size;
inline int read(int x=0)
{
    char c=getchar();
    while(c<48 or c>57)c=getchar();
    while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48, c=getchar();
    return x;
}
inline char readc()
{
    char c=getchar();
    while(c^'A' and c^'M')c=getchar();
    return c;
}
inline void init()
{
    int i, l, r;
    N=read(), Q=read();
    for(i=1;i<=N;i++)tmp[i]=a[i]=read();
    for(size=1;(1<<size)*(1<<size)<=N;size++);size--;
    for(i=0;i<=N>>size;i++)
    {
        l=i<<size, r=(i+1<<size)-1;
        sort(tmp+l,tmp+r+1);
    }
}
inline void pushdown(int block)
{
    int l=block<<size, r=(block+1<<size)-1, i;
    for(i=l;i<=r;i++)a[i]+=tag[block], tmp[i]+=tag[block];
    tag[block]=0;
}
inline void rebuild(int block)
{
    int l=block<<size, r=(block+1<<size)-1, i;
    for(i=l;i<=r;i++)tmp[i]=a[i];
    sort(tmp+l,tmp+r+1);
}
inline void add(int l, int r, int w)
{
    int i;
    for(i=(l>>size)+1;i<r>>size;i++)tag[i]+=w;
    pushdown(l>>size), pushdown(r>>size);
    for(i=l;i>>size==l>>size and i<=r;i++)a[i]+=w;
    if((l>>size)^(r>>size))for(i=r;i>>size==r>>size;i--)a[i]+=w;
    rebuild(l>>size);if((l>>size)^(r>>size))rebuild(r>>size);
}
inline int bin(int block, int w)
{
    int l, r, mid;
    l=block<<size, r=(block+1<<size)-1;
    if(tmp[r]<w)return 0;
    while(l^r)
    {
        mid=(l+r)>>1;
        if(tmp[mid]>=w)r=mid;
        else l=mid+1;
    }
    return (block+1<<size)-1 - l + 1;
}
inline void quiry(int l, int r, int c)
{
    int i, ans=0;
    for(i=(l>>size)+1;i<r>>size;i++)ans+=bin(i,c-tag[i]);
    pushdown(l>>size), pushdown(r>>size);
    for(i=l;i>>size==l>>size and i<=r;i++)if(a[i]>=c)ans++;
    if((l>>size)^(r>>size))for(i=r;i>>size==r>>size;i--)if(a[i]>=c)ans++;
    printf("%d\n",ans);
}
int main()
{
    char type;
    int l, r, x;
    init();
    for(int i=1;i<=Q;i++)
    {
        type=readc();
        l=read(), r=read(), x=read();
        if(type=='M')add(l,r,x);
        else quiry(l,r,x);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值