[BZOJ]3343: 教主的魔法 分块+二分

Description

教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1、2、……、N。
每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)
CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。
WD巨懒,于是他把这个回答的任务交给了你。

题解:

好久没有分块了,水了一道分块基础练习题。每一块开个数组,排好序,区间加的话,同一块就暴力加,重新排序;否则两端不完整的块暴力加,中间完整的块打标记;询问的话,同一块暴力搞;不同块的话,两端不完整的暴力搞,中间完整的在块内二分即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=1000010;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
int n,q,sz,bel[Maxn],L[Maxn],R[Maxn],a[Maxn],b[1005][1005],len[1005],lazy[1005];
int main()
{
    n=read();q=read();
    int sz=(int)(sqrt(n));bel[0]=-1;
    for(int i=1;i<=n;i++)
    {
        bel[i]=(i-1)/sz+1;
        if(bel[i]!=bel[i-1])L[bel[i]]=i;
        R[bel[i]]=i;
    }
    for(int i=1;i<=n;i++)a[i]=read(),b[bel[i]][++len[bel[i]]]=a[i];
    for(int i=1;i<=bel[n];i++)sort(b[i]+1,b[i]+1+len[i]);
    while(q--)
    {
        char op[3];
        scanf("%s",op);
        int x=read(),y=read(),z=read();
        if(op[0]=='M')
        {
            if(bel[x]==bel[y])
            {
                int o=bel[x];
                for(int i=x;i<=y;i++)b[o][i-(o-1)*sz]+=z,a[i]+=z;
                sort(b[o]+1,b[o]+1+len[o]);
            }
            else
            {
                int o;
                o=bel[x];
                for(int i=x;i<=R[o];i++)b[o][i-(o-1)*sz]+=z,a[i]+=z;
                sort(b[o]+1,b[o]+1+len[o]);
                o=bel[y];
                for(int i=L[o];i<=y;i++)b[o][i-(o-1)*sz]+=z,a[i]+=z;
                sort(b[o]+1,b[o]+1+len[o]);
                for(int i=bel[x]+1;i<bel[y];i++)lazy[i]+=z;
            }
        }
        else
        {
            int ans=0;
            if(bel[x]==bel[y])
            {
                int o=bel[x];
                for(int i=x;i<=y;i++)if(a[i]+lazy[o]>=z)ans++;
            }
            else
            {
                int o;
                o=bel[x];
                for(int i=x;i<=R[o];i++)if(a[i]+lazy[o]>=z)ans++;
                o=bel[y];
                for(int i=L[o];i<=y;i++)if(a[i]+lazy[o]>=z)ans++;
                for(int i=bel[x]+1;i<bel[y];i++)
                {
                    int l=1,r=len[i];
                    while(l<=r)
                    {
                        int mid=l+r>>1;
                        if(b[i][mid]+lazy[i]>=z)r=mid-1;
                        else l=mid+1;
                    }
                    ans+=(len[i]-r);
                }
            }printf("%d\n",ans);
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值