原题地址:https://loj.ac/problem/6279
题意:给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的前驱(比其小的最大元素)。
思路:和区间加法和分块2的差不多。我们只需要对分块2的查询操作做修改就行了。
注意:除了可以像我下面的代码一样使用vector来排序查找一样,还可以使用set来维护块中的数据。只不过将先清空再插入,改为了先删除set中的元素在插入新的元素。
同样引用作者hzwer的话来说,数据n<=100000其实是为了区分暴力和一些常数较大的写法。
接着第二题的解法,其实只要把块内查询的二分稍作修改即可。
不过这题其实想表达:可以在块内维护其它结构使其更具有拓展性,比如放一个 set ,这样如果还有插入、删除元素的操作,会更加的方便。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
typedef long long ll;
const int inf =0x3f3f3f3f;
int n, num, block, belog[maxn], opt, l, r, c;//belog[i]=j,表示a[i]属于第j个分块
int a[maxn], sum[maxn] ;//sum是整块的加法标记
vector<int>v[maxn];//用于排序
vector<int>::iterator it;
inline int read() {//读入挂
int ret = 0, c, f = 1;
for(c = getchar(); !(isdigit(c) || c == '-'); c = getchar());
if(c == '-') f = -1, c = getchar();
for(; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
if(f < 0) ret = -ret;
return ret;
}
void reset(int x) {//对第x个分块整体排序
v[x].clear();
for(int i = (x - 1) * block + 1; i <= min(x * block, n); i++) {
//因为是整体,所以是i <= min(x * block, n),这样写还是因为最后一个分块可能并灭有block个元素
v[x].push_back(a[i]);
}
sort(v[x].begin(), v[x].end());
}
void updata(int l, int r, int c) {
int t1 = belog[l];
int t2 = belog[r];
for(int i = l; i <= min(t1 * block, r); i++) a[i] += c;//对第一个不完整区间暴力加法
reset(t1);//对一个修改区间排序
if(t1 != t2) {
for(int i = (t2 - 1) * block + 1; i <= r; i++) {//对最后一个区间做暴力加法
a[i] += c;
}
reset(t2);//对最后一个区间排序
}
for(int i = t1 + 1; i <= t2 - 1; i++) sum[i] += c;//对整块加上加法标记
}
void query(int l, int r, int c) {
int t1 = belog[l];
int t2 = belog[r];
int ret = -inf;
int flag = 0;//用于标记有没有比c小的数
for(int i = l; i <= min(t1 * block, r); i++) {
if(a[i] + sum[t1] < c) {
ret = max(a[i] + sum[t1], ret);
flag = 1;
}
}
if(t1 != t2) {
for(int i = (t2 - 1) * block + 1; i <= r; i++) {
if(a[i] + sum[t2] < c) {
ret = max(a[i] + sum[t2], ret);
flag = 1;
}
}
for(int i = t1 + 1; i <= t2 - 1; i++) {
int x = c - sum[i];
it = lower_bound(v[i].begin(), v[i].end(), x);
if(it != v[i].begin()) {
/*lower_bound返回的是第一个>=查找元素的,因此如果返回的v[i].begin(),
那就说明在当前块中没有比待查找元素小的
相反如果不是返回v[i].begin(),那就说明存在,因为返回的是>=的值,因此要做
比较的时候需要it-1.
*/
flag=1;
ret=max(ret,*(it-1)+sum[i]);//在更新的时候别忘了要加上sum[i]
}
}
}
if(!flag) printf("-1\n");
else printf("%d\n", ret);
}
int main() {
n = read();
block = 2*sqrt(n);
for(int i = 1; i <= n; i++) {
a[i] = read();
belog[i] = (i - 1) / block + 1;
v[belog[i]].push_back(a[i]);
}
for(int i = 1; i <= belog[n]; i++) {
sort(v[i].begin(), v[i].end());
}
for(int j = 1; j <= n; j++) {
opt = read();
l = read();
r = read();
c = read();
if(opt) query(l, r, c );
else updata(l, r, c);
}
return 0;
}