分块2----#6278. 数列分块入门 2

题目链接:https://loj.ac/problem/6278

题目描述

给出一个长为 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}1≤n≤50000,−231≤others、\mathrm{ans} \leq 2^{31}-1ans≤231−1。

思路:第一题是查询一个数的,而这题是要在一个区间内找到一个比c*c小的值。暴力一个一个找肯定是会超时的。我们可以接着第一题分块的思路查询,左右不足一块的排序暴力找就行了,而整块的我们排序二分找就很快了。这里要再建一个数组(这里用的vector,因为二分可以用库函数lower_bound())保存改变的值用来排序。

ac代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include<vector>
#include<set>
#include<bits/stdc++.h>
using namespace std;
int m,n,pos[51000];
int s[51000],flag[51000];
vector<int>v[550];

void reset(int x)//用来不构成一整块的排序 
{
    v[pos[x]].clear();
    for(int i=(pos[x]-1)*m+1;i<=min(pos[x]*m,n);i++)
        v[pos[x]].push_back(s[i]);
    sort(v[pos[x]].begin(),v[pos[x]].end());
}

void change(int a,int b,int c)//更新值 
{
    for(int i=a;i<=min(pos[a]*m,b);i++)//左边不成一整块,暴力加 
        s[i]+=c;
    reset(a);
    if(pos[a]!=pos[b])
    {
        for(int i=(pos[b]-1)*m+1;i<=b;i++)//右边不成一整块的暴力加    
            s[i]+=c;
        reset(b);
    }    
    for(int i=pos[a]+1;i<=pos[b]-1;i++)//中间整块的整块加,用flag数组保存 
        flag[i]+=c;
}

int query(int l,int r,int c)//查询答案 
{
    int ans=0;
    for(int i=l;i<=min(pos[l]*m,r);i++)//左边不构成一整块的暴力查找 
        if(s[i]+flag[pos[l]]<c)
            ans++;
    if(pos[l]!=pos[r])
    {
        for(int i=(pos[r]-1)*m+1;i<=r;i++)//右边不构成一整块的暴力查找 
            if(s[i]+flag[pos[r]]<c) 
                ans++;
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++)//中间整块的二分查找 
    {
        int x=c-flag[i];
        ans+=lower_bound(v[i].begin(),v[i].end(),x)-v[i].begin();
    }
    return ans;
}

int main()
{
    scanf("%d",&n);
    m=sqrt(n);
    for (int i=1;i<=n;i++) pos[i]=(i-1)/m+1;
    
    for (int i=1;i<=n;i++) scanf("%d",&s[i]);
    
    for(int i=1;i<=n;i++)
        pos[i]=(i-1)/m+1,v[pos[i]].push_back(s[i]);//每个数分块的序号,放入vector保存 
        
    for(int i=1;i<=pos[n];i++)
        sort(v[i].begin(),v[i].end());//整块排序 
        
    for (int i=1;i<=n;i++)
    {
        int x,a,b,c;
        scanf("%d%d%d%d",&x,&a,&b,&c);
        if (x==0)
        {
            change(a,b,c);
        }
        else if (x==1) //查询块里不大于c平方的数 ,完整块用的是二分思想,不完整块直接暴力 
        {
            cout<<query(a,b,c*c)<<endl;
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值