数列分块入门 2

题目描述
给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。

输入格式
第一行输入一个数字n。

第二行输入n个数字,第i个数字为ai,以空格隔开。

接下来输入n行询问,每行输入四个数字 opt、l、r、c,以空格隔开。

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

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

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

样例
样例输入
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%的数据,
1≤n≤50000,
-2^31 ≤ others、ans ≤ 2^31-1

#include <iostream>
#include <string>
#include <cstring>
#include <ctime>
#include <fstream>
#include <cctype>
#include <vector>
#include <algorithm>
#include <math.h>
using namespace std;
// tip:  belong[i]=(i-1)/sz+1;   //sz指每一块的大小
int a[500010];
int belong[100010];
int sz;
int atag[100010];
int ans;
vector<int> G[500010];   //G[i]指第i块
int n;
void reset(int x) {    //让第x块重新以从小到大排序
    G[x].clear();
    for (int i = (x - 1) * sz + 1; i <= min(x * sz, n); i++) G[x].push_back(a[i]);    //把第i组的数重新压入G[i]
    sort(G[x].begin(), G[x].end());  
}
void add(int l, int r, int c) {   
    for (int i = l; i <= min(belong[l] * sz, r); i++) a[i] += c;
    reset(belong[l]);    //第l块的数据已经改变,重新设置数组顺序
    if (belong[l] != belong[r]) {
        for (int i = (belong[r] - 1) * sz + 1; i <= r; i++) a[i] += c;
        reset(belong[r]);  //同上
    }
    for (int i = belong[l] + 1; i <= belong[r] - 1; i++) {
        atag[i] += c;   //没有对原数据a[i]进行处理,因此不需要reset
    }
}
int inquire(int l, int r, int c) {   //查询
    for (int i = l; i <= min(belong[l] * sz, r); i++) {
        if (a[i] + atag[belong[i]] < c)
            ans++;
    }
    if (belong[l] != belong[r]) {
        for (int i = (belong[r] - 1) * sz + 1; i <= r; i++) {
            if (a[i] + atag[belong[r]] < c)
                ans++;
        };
    }
    for (int i = belong[l] + 1; i <= belong[r] - 1; i++) {
        ans += lower_bound(G[i].begin(), G[i].end(), c - atag[i]) - G[i].begin();  //注意要的是lower_bound而不是upper_bound,注意地址大小与个数之间的转换
    }
    return ans;
}
int main() {
    while (cin >> n) {
        int m = n;
        sz = sqrt(n);
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            belong[i] = (i - 1) / sz + 1;
            G[belong[i]].push_back(a[i]);
        }
        memset(atag, 0, sizeof(atag));
        for (int i = 1; i <= belong[n]; i++) sort(G[i].begin(), G[i].end()); //排序好序才能进行二分查找
        while (m--) {
            int opt, l, r, c;
            cin >> opt >> l >> r >> c;
            if (opt == 0) {
                add(l, r, c);
            } else if (opt == 1) {
                ans = 0;
                ans = inquire(l, r, c * c);
                cout << ans << endl;
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值