数列分块入门2--LOJ(区间小于x的个数)

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

内存限制:256 MiB时间限制:500 ms

题目描述

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

输入格式

第一行输入一个数字 。

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

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

若pot =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.


这题和上一题差不多,只不过有个操作比较难以完成,就是查找区间小于x数的个数。

按照分块一贯的尿性,我们先将数列分为sqrt(n)块,然后计算一下它的块的端点,和每个点所属的块,我们这里用另外一个数组d复制一下a

int n;
cin >> n;
int t = sqrt(n);
for (int i = 1; i <= n; i++) cin >> a[i], d[i] = a[i];
for (int i = 1; i <= t; i++) {
     L[i] = (i - 1) * t + 1;
     R[i] = i * t;
}
if (R[t] < n)
    t++, L[t] = R[t - 1] + 1, R[t] = n;
for (int i = 1; i <= t; i++)
    for (int j = L[i]; j <= R[i]; j++) id[j] = i;

接下来我们要干什么呢?当然是将每个块有序化:

for (int i = 1; i <= t; i++) 
    sort(d + L[i], d + R[i] + 1);

这样操作之后每个块内的元素都是有序的,但又不影响原来的数组。

接下来就是常规操作了,对于update而言,我们在分块1的基础上多了一个操作:

int ll = id[l], rr = id[r];
for (int i = l; i <= R[ll]; i++) a[i] += w;
for (int i = L[ll]; i <= R[ll]; i++) d[i] = a[i];
sort(d + L[ll], d + R[ll] + 1);

也就是说l所属的块的元素的顺序被打乱了,我们需要重新排序。对于不属于l和r的块我们就不需要做任何操作,因为块中的每个元素加上同一个数的大小关系是不变的。

那么询问的时候也是老样子,对于l和r所属的块暴力查询,对l到r之间的块,由于它们是有序的,我们只需要找到第一个大于等于他的就好了,所以我们二分一下就出来了:

for (int i = ll + 1; i <= rr - 1; i++) {
     int x = w - add[i];
     ans += lower_bound(d + L[i], d + R[i] + 1, x) - (d + L[i]);
}

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

const int mac = 5e4 + 10;

int a[mac], add[300], id[mac], L[mac], R[mac];
int d[mac];

void update(int l, int r, int w) {
    int ll = id[l], rr = id[r];
    if (ll == rr) {
        for (int i = l; i <= r; i++) a[i] += w;
        for (int i = L[ll]; i <= R[ll]; i++) d[i] = a[i];
        sort(d + L[ll], d + R[ll] + 1);
    } else {
        for (int i = l; i <= R[ll]; i++) a[i] += w;
        for (int i = L[ll]; i <= R[ll]; i++) d[i] = a[i];
        sort(d + L[ll], d + R[ll] + 1);

        for (int i = ll + 1; i <= rr - 1; i++) add[i] += w;

        for (int i = L[rr]; i <= r; i++) a[i] += w;
        for (int i = L[rr]; i <= R[rr]; i++) d[i] = a[i];
        sort(d + L[rr], d + R[rr] + 1);
    }
}

int query(int l, int r, int w) {
    int ll = id[l], rr = id[r];
    int ans = 0;
    if (ll == rr) {
        for (int i = l; i <= r; i++)
            if (a[i] + add[ll] < w)
                ans++;
    } else {
        for (int i = l; i <= R[ll]; i++)
            if (a[i] + add[ll] < w)
                ans++;
        for (int i = L[rr]; i <= r; i++)
            if (a[i] + add[rr] < w)
                ans++;

        for (int i = ll + 1; i <= rr - 1; i++) {
            int x = w - add[i];
            ans += lower_bound(d + L[i], d + R[i] + 1, x) - (d + L[i]);
        }
    }
    return ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    int t = sqrt(n);
    for (int i = 1; i <= n; i++) cin >> a[i], d[i] = a[i];
    for (int i = 1; i <= t; i++) {
        L[i] = (i - 1) * t + 1;
        R[i] = i * t;
    }
    if (R[t] < n)
        t++, L[t] = R[t - 1] + 1, R[t] = n;
    for (int i = 1; i <= t; i++)
        for (int j = L[i]; j <= R[i]; j++) id[j] = i;
    for (int i = 1; i <= t; i++) sort(d + L[i], d + R[i] + 1);
    for (int i = 1; i <= n; i++) {
        int opt, l, r, c;
        cin >> opt >> l >> r >> c;
        if (opt) {
            int ans = query(l, r, c * c);
            cout << ans << endl;
        } 
        else update(l, r, c);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值