分块(块状数组)

洛谷P2801 教主的魔法
题目描述
教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给 XMYZ 信息组每个英雄看。于是 NN 个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为 1, 2, \ldots, N1,2,…,N。

每个人的身高一开始都是不超过 10001000 的正整数。教主的魔法每次可以把闭区间 [L, R][L,R](1≤L≤R≤N1≤L≤R≤N)内的英雄的身高全部加上一个整数 WW。(虽然 L=RL=R 时并不符合区间的书写规范,但我们可以认为是单独增加第 L®L® 个英雄的身高)

CYZ、光哥和 ZJQ 等人不信教主的邪,于是他们有时候会问 WD 闭区间 [L, R][L,R] 内有多少英雄身高大于等于 CC,以验证教主的魔法是否真的有效。

WD 巨懒,于是他把这个回答的任务交给了你。

输入格式
第 11 行为两个整数 N, QN,Q。QQ 为问题数与教主的施法数总和。

第 22 行有 NN 个正整数,第 ii 个数代表第 ii 个英雄的身高。

第 33 到第 Q+2Q+2 行每行有一个操作:

若第一个字母为 M,则紧接着有三个数字 L, R, WL,R,W。表示对闭区间 [L, R][L,R] 内所有英雄的身高加上 WW。

若第一个字母为 A,则紧接着有三个数字 L, R, CL,R,C。询问闭区间 [L, R][L,R] 内有多少英雄的身高大于等于 CC。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<set>
#include<string.h>
#include<vector>
#include<set>
#include<cmath>
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define ll long long
#define inf 0x3f3f3f3f
const int maxn=1e6+5,SQ=1005;
using namespace std;
int st[SQ],ed[SQ],si[SQ],A[maxn],bel[maxn ];
 int read()
{
    int ans=0;
    char c=getchar();
    while(!isdigit(c))
    {
        c=getchar();
    }
    while(isdigit(c))
    {
        ans=ans*10+c-'0';
        c=getchar();
    }
    return ans;
}
void init(int n)
{
    int sq=sqrt(n);
    for(int i=1;i<=sq;i++)
    {
        st[i]=n/sq * (i-1)+1;
        ed[i]=n/sq * i;
    }
    ed[sq]=n;
    for(int i=1;i<=sq;i++)
        for(int j=st[i];j<=ed[i];j++)
             bel[j]=i;
    for(int i=1;i<=sq;i++)
        si[i]=ed[i]-st[i]+1;
}
int mark[SQ];
vector<int>v[SQ];
void update(int b)
{
    for(int i=0;i<si[b];i++)
        v[b][i]=A[st[b]+i];
    sort(v[b].begin(),v[b].end());
}
int main()
{
    int n=read(),m=read();
    int sq=sqrt(n);
    init(n);
    for(int i=1;i<=n;i++)
    {
        A[i]=read();
    }
    for(int i=1;i<=sq;i++)
        for(int j=st[i];j<=ed[i];j++)
            v[i].push_back(A[j]);
    for(int i=1;i<=sq;i++)sort(v[i].begin(),v[i].end());
    while(m--)
    {
        char o;
        scanf(" %c", &o);
        int l=read(),r=read(),k=read();
        if(o=='M')
        {
           if(bel[l]==bel[r])
           {
               for(int i=l;i<=r;i++)
               {
                   A[i]+=k;
               }
               update(bel[l]);
               continue;
           }
           for(int i=l;i<=ed[bel[l]];i++)A[i]+=k;
           for(int i=st[bel[r]];i<=r;i++)A[i]+=k;
           update(bel[l]);
           update(bel[r]);
           for(int i=bel[l]+1;i<bel[r];i++)mark[i]+=k;
        }
        else
        {
            int tot=0;
            if(bel[l]==bel[r])
            {
               for(int i=l;i<=r;i++)
                if(A[i]+mark[bel[l]]>=k)tot++;
               printf("%d\n",tot);
               continue;
            }
            for(int i=l;i<=ed[bel[l]];i++)
                if(A[i]+mark[bel[l]]>=k)tot++;
            for(int i=st[bel[r]];i<=r;i++)
                if(A[i]+mark[bel[r]]>=k)tot++;
            for(int i=bel[l]+1;i<bel[r];i++)
            {
            tot+=v[i].end()-lower_bound(v[i].begin(),v[i].end(),k-mark[i]);
            }
            printf("%d\n",tot);
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值