LOJ6279. 数列分块入门 3 题解
涉及操作:
- 区间加法;
- 区间询问某个数 \(x\) 的前驱(比其小的最大元素)。
解题思路:
数列分块。思路和第2题思路几乎相同,也是每一段副本排序。完整的分块进行二分查找。
示例程序:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
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) {
bool exist = false;
int ans;
for (int i = l; i <= min(bl[l]*blo, r); i ++) {
if (a[i] + tag[bl[i]] < val) {
if (!exist || ans < a[i] + tag[bl[i]]) {
exist = true;
ans = a[i] + tag[bl[i]];
}
}
}
if (bl[l] != bl[r]) {
for (int i = (bl[r]-1)*blo+1; i <= r; i ++) {
if (a[i] + tag[bl[i]] < val) {
if (!exist || ans < a[i] + tag[bl[i]]) {
exist = true;
ans = a[i] + tag[bl[i]];
}
}
}
}
for (int i = bl[l]+1; i < bl[r]; i ++) {
int p = lower_bound(vec[i].begin(), vec[i].end(), val-tag[i]) - vec[i].begin();
if (p) {
int x = vec[i][p-1] + tag[i];
if (!exist || ans < x) {
exist = true;
ans = x;
}
}
}
if (!exist) return -1;
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) << endl;
}
}
return 0;
}