LOJ #6278. 数列分块入门 2-分块(区间加法、查询区间内小于某个值x的元素个数)...

本文详细介绍了一种解决数列区间操作与查询问题的有效算法——数列分块。通过实例讲解了如何使用该算法处理区间加法及查询区间内小于特定值的元素数量,适用于大规模数据集的高效处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内存限制:256 MiB时间限制:500 ms标准输入输出
题目类型:传统评测方式:文本比较
上传者: hzwer
题目描述

给出一个长为 nn 的数列,以及 nn 个操作,操作涉及区间加法,询问区间内小于某个值 xx 的元素个数。

输入格式

第一行输入一个数字 nn。

第二行输入 nn 个数字,第 ii 个数字为 a_iai,以空格隔开。

接下来输入 nn 行询问,每行输入四个数字 \mathrm{opt}opt、ll、rr、cc,以空格隔开。

若 \mathrm{opt} = 0opt=0,表示将位于 [l, r][l,r] 的之间的数字都加 cc。

若 \mathrm{opt} = 1opt=1,表示询问 [l, r][l,r] 中,小于 c^2c2 的数字的个数。

输出格式

对于每次询问,输出一行一个数字表示答案。

样例
样例输入
4
1 2 2 3
0 1 3 1
1 1 3 2
1 1 4 1
1 2 3 2
样例输出
3
0
2
数据范围与提示

对于 100\%100% 的数据,1 \leq n \leq 50000, -2^{31} \leq \mathrm{others}1n50000,231others、\mathrm{ans} \leq 2^{31}-1ans2311。

 

代码:

//#6278. 数列分块入门 2-区间加法,查询区间内小于某个值x的元素个数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e4+10;

int n,m;
ll a[maxn],b[maxn],pos[maxn],tag[maxn];

void rechange(int x)
{
    for(int i=(x-1)*m+1;i<=min(x*m,n);i++){
            b[i]=a[i];
    }
    sort(b+(x-1)*m+1,b+min(x*m,n)+1);
}

void update(int l,int r,ll c)
{
    if(pos[l]==pos[r]){
        for(int i=l;i<=r;i++)
            a[i]+=c;
        rechange(pos[l]);
    }
    else{
        for(int i=l;i<=pos[l]*m;i++)
            a[i]+=c;
        rechange(pos[l]);
        for(int i=pos[l]+1;i<=pos[r]-1;i++)
            tag[i]+=c;
        for(int i=(pos[r]-1)*m+1;i<=r;i++)
            a[i]+=c;
        rechange(pos[r]);
    }
}

int getnum(int l,int r,int c)
{
    int num=0;
    if(pos[l]==pos[r]){
        for(int i=l;i<=r;i++){
            if(a[i]+tag[pos[l]]<c) num++;
        }
    }
    else{
        for(int i=l;i<=pos[l]*m;i++){
            if(a[i]+tag[pos[l]]<c) num++;
        }
        for(int i=pos[l]+1;i<=pos[r]-1;i++){
            int cnt=c-tag[i];
            num+=lower_bound(b+(i-1)*m+1,b+i*m+1,cnt)-b-(i-1)*m-1;//少减一wa
        }
        for(int i=(pos[r]-1)*m+1;i<=r;i++){
            if(a[i]+tag[pos[r]]<c) num++;
        }
    }
    return num;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    m=sqrt(n);
    for(int i=1;i<=n;i++)
        pos[i]=(i-1)/m+1;
    for(int i=1;i<=m+1;i++){
        sort(b+(i-1)*m+1,b+min(i*m,n)+1);
    }
    for(int i=1;i<=n;i++){
        int op,l,r;
        ll c;
        scanf("%d%d%d%lld",&op,&l,&r,&c);
        if(op==0){
            update(l,r,c);
        }
        else{
            printf("%d\n",getnum(l,r,c*c));
        }
    }
    return 0;
}


/*
5
1 3 7 2 5
0 1 3 1
1 1 3 2
1 1 4 1
1 2 3 2
1 3 5 4

1
0
0
3
*/

/*
10
1 3 4 2 5 7 11 3 5 1
0 1 5 1
1 1 7 2
0 3 9 1
1 4 8 2
1 1 10 2
1 3 5 3
1 5 10 3
1 6 10 2
1 2 7 2
1 2 7 3


2
0
2
3
5
1
0
5
*/

 

 

 

 

转载于:https://www.cnblogs.com/ZERO-/p/10525630.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值