1. 程式人生 > 其它 >LOJ6278. 數列分塊入門 2 題解

LOJ6278. 數列分塊入門 2 題解

題目連結:https://loj.ac/p/6278

涉及操作:

  1. 區間加法;
  2. 區間詢問小於某個值 \(x\) 的數的個數。

解題思路:

數列分塊。

每個分塊儲存一個副本並排序(程式中用 vector 容器 vec[i] 來儲存第 i 個分塊中的資訊)。

示例程式:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 50050;
int n, blo, a[maxn], bl[maxn], tag[505];
vector<int> vec[505];

// 重置第p個分塊
void reset(int p) {
    vec[p].clear();
    for (int i = (p-1)*blo+1; i <= min(p*blo, n); i ++)
        vec[p].push_back(a[i]);
    sort(vec[p].begin(), vec[p].end());
}

// 區間 [l,r] + val
void add(int l, int r, int val) {
    for (int i = l; i <= min(bl[l]*blo, r); i ++)
        a[i] += val;
    reset(bl[l]);
    if (bl[l] != bl[r]) {
        for (int i = (bl[r]-1)*blo+1; i <= r; i ++)
            a[i] += val;
        reset(bl[r]);
    }
    for (int i = bl[l]+1; i < bl[r]; i ++)
        tag[i] += val;
}

// 返回區間 [l,r] 內小於 val 的數的個數
int query(int l, int r, int val) {
    int ans = 0;
    for (int i = l; i <= min(bl[l]*blo, r); i ++)
        if (a[i] + tag[bl[i]] < val)
            ans ++;
    if (bl[l] != bl[r]) {
        for (int i = (bl[r]-1)*blo+1; i <= r; i ++)
            if (a[i] + tag[bl[i]] < val)
                ans ++;
    }
    for (int i = bl[l]+1; i < bl[r]; i ++) {
        ans += lower_bound(vec[i].begin(), vec[i].end(), val-tag[i]) - vec[i].begin();
    }
    return ans;
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n;
    blo = sqrt(n);
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        bl[i] = (i - 1) / blo + 1;
    }
    for (int i = 1; i <= bl[n]; i ++)
        reset(i);
    for (int i = 0; i < n; i ++) {
        int op, l, r, c;
        cin >> op >> l >> r >> c;
        if (op == 0) {
            add(l, r, c);
        }
        else {
            cout << query(l, r, c*c) << endl;
        }
    }
    return 0;
}