题面
题目描述
给定一个长为 (n) 的数列,以及 (n) 个操作,操作涉及区间加法,询问区间内小于某个值 (x) 的前驱(比其小的最大元素)。
输入格式
第一行输入一个数字 (n)。
第二行输入 (n) 个数字,第 (i) 个数字为 (a_i),以空格隔开。
接下来输入 (n) 行询问,每行输入四个数字 ( m{opt}、l、r、c),以空格隔开。
若 ( m opt=0),表示将位于 (left[l, r ight]) 的之间的数字都加 (c)。
若 ( m opt=1),表示询问 (left[l, r ight]) 中 (c) 的前驱的值(不存在则输出 ( m -1))。
输出格式
对于每次询问,输出一行一个数字表示答案。
样例(很水)
Input
4
1 2 2 3
0 1 3 1
1 1 4 4
0 1 2 2
1 1 2 4
Output
3
-1
数据范围与提示
对于 (100\%) 的数据,(1leq nleq 100000,-2^{31}leq { m others})、({ m ans}leq 2^{31}-1)。
对题目的吐槽
这个样例是真的水,从样例中根本找不出自己程序任何错误。。。。
我一开始程序内层循环用的是i变量,样例居然给我过了?!
思路
审题
这道题他说要求前驱的值。要求前驱的值如果直接暴力查找的话肯定会T飞,所以需要用到分块来优化。
解法
加c的核心代码如下:
for (int j = x; j <= r[b[x]]; j++) a[j] += k; //左边不完整块暴力相加
for (int j = l[b[x]]; j <= r[b[x]]; j++) d[j] = a[j]; //将a[]数组复制一份给d[]数组,以此来达到将数组排序而不破坏原数组顺序
sort(d + l[b[x]], d + r[b[x]] + 1); //将d[]数组排序
for (int j = l[b[y]]; j <= y; j++) a[j] += k; //右边不完整块暴力相加
for (int j = l[b[y]]; j <= r[b[y]]; j++) d[j] = a[j]; //将a[]数组复制一份给d[]数组,以此来达到将数组排序而不破坏原数组顺序
sort(d + l[b[y]], d + r[b[y]] + 1); //将d[]数组排序
for (int j = b[x] + 1; j <= b[y] - 1; j++) lazy[j] += k; //用lazyp[]数组对中间完整块做标记,减少运算量
查询的核心代码如下:
for (int j = x; j <= r[b[x]]; j++) //暴力查询左边不完整块
if ((lazy[b[x]] + a[j] < k) && (lazy[b[x]] + a[j] > maxx)/*需要保证这个比k小并且比maxx大*/)
maxx = lazy[b[x]] + a[j];
for (int j = l[b[y]]; j <= y; j++) //暴力查询右边不完整块
if ((lazy[b[y]] + a[j] < k) && (lazy[b[y]] + a[j] > maxx))
maxx = lazy[b[y]] + a[j];
for (int j = b[x] + 1; j <= b[y] - 1; j++) {
if (k - lazy[j] <= d[(j - 1) * block + 1]) continue;
int num = lower_bound(d + l[j], d + r[j] + 1, k - lazy[j]) - d - 1;
//lower_bound函数返回的是大于或等于k - lazy[j]的第一个数的地址(指针),减去d可得数组下标,再减一即可得到前驱
maxx = maxx > (d[num] + lazy[j]) ? maxx : (d[num] + lazy[j]);
}
if (maxx == -1) write(-1); //如果maxx没变(即没找到),则输出-1
else write(maxx); //否则输出maxx(前驱)
代码
这次的代码是LibreOJ格式化过的,总觉得有亿点奇怪
#include <iostream>
#include <algorithm> //sort()要用
#include <cstdio>
#include <cmath> //sqrt()y要用
#define int long long
using namespace std;
int a[1000005], d[1000005], l[1005], r[1005], b[1000005], lazy[1005];
int n, q, block, tot, x, y, k;
int c;
inline int read() { //快读
int X = 0;
bool flag = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
flag = 0;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
X = (X << 1) + (X << 3) + ch - '0';
ch = getchar();
}
if (flag)
return X;
return ~(X - 1);
}
inline void write(int X) { //快写
if (X < 0) {
putchar('-');
X = ~(X - 1);
}
int s[50], top = 0;
while (X) {
s[++top] = X % 10;
X /= 10;
}
if (!top)
s[++top] = 0;
while (top)
putchar(s[top--] + '0');
putchar('
');
return;
}
signed main() {
n = read();
for (int i = 1; i <= n; i++)
a[i] = read();
block = sqrt(n), tot = n / block;
if (n % block)
tot++; //如果数的个数不是块长的倍数的话,还要再增加一个块的个数
for (int i = 1; i <= n; i++)
b[i] = (i - 1) / block + 1, d[i] = a[i];
for (int i = 1; i <= tot; i++)
l[i] = (i - 1) * block + 1, r[i] = i * block;
r[tot] = n;
for (int i = 1; i <= tot; i++)
sort(d + l[i], d + r[i] + 1);
for (int i = 1; i <= n; i++) {
c = read();
x = read();
y = read();
k = read();
if (c == 0) {
if (b[x] == b[y]) { //如果x和y在同一个块内,就直接只能暴力相加
for (int j = x; j <= y; j++)
a[j] += k;
for (int j = l[b[x]]; j <= r[b[x]]; j++)
d[j] = a[j];
sort(d + l[b[x]], d + r[b[x]] + 1);
} else {
for (int j = x; j <= r[b[x]]; j++) //左边不完整块暴力相加
a[j] += k;
for (int j = l[b[x]]; j <= r[b[x]]; j++) //将a[]数组复制一份给d[]数组,以此来达到将数组排序而不破坏原数组顺序
d[j] = a[j];
sort(d + l[b[x]], d + r[b[x]] + 1); //将d[]数组排序
for (int j = l[b[y]]; j <= y; j++) //右边不完整块暴力相加
a[j] += k;
for (int j = l[b[y]]; j <= r[b[y]]; j++) //将a[]数组复制一份给d[]数组,以此来达到将数组排序而不破坏原数组顺序
d[j] = a[j];
sort(d + l[b[y]], d + r[b[y]] + 1); //将d[]数组排序
for (int j = b[x] + 1; j <= b[y] - 1; j++) //用lazyp[]数组对中间完整块做标记,减少运算量
lazy[j] += k;
}
}
if (c == 1) {
int maxx = -1;
if (b[x] == b[y]) { //如果x和y在同一个块内,就直接只能暴力求解
for (int j = x; j <= y; j++)
if ((lazy[b[x]] + a[j] < k) && (lazy[b[x]] + a[j] > maxx))
maxx = lazy[b[x]] + a[j];
if (maxx == -1)
write(-1);
else
write(maxx);
continue;
} else {
for (int j = x; j <= r[b[x]]; j++) //暴力查询左边不完整块
if ((lazy[b[x]] + a[j] < k) && (lazy[b[x]] + a[j] > maxx))
maxx = lazy[b[x]] + a[j];
for (int j = l[b[y]]; j <= y; j++) //暴力查询右边不完整块
if ((lazy[b[y]] + a[j] < k) && (lazy[b[y]] + a[j] > maxx))
maxx = lazy[b[y]] + a[j];
for (int j = b[x] + 1; j <= b[y] - 1; j++) {
if (k - lazy[j] <= d[(j - 1) * block + 1])
continue;
int num = lower_bound(d + l[j], d + r[j] + 1, k - lazy[j]) - d - 1;
//lower_bound函数返回的是大于或等于k - lazy[j]的第一个数的地址(指针),减去d可得数组下标,再减一即可得到前驱
maxx = (maxx > (d[num] + lazy[j])) ? maxx : (d[num] + lazy[j]);
}
if (maxx == -1) //如果maxx没变(即没找到),则输出-1
write(-1);
else
write(maxx); //否则输出maxx(前驱)
}
}
}
return 0;
}