1649 字
8 分钟
【题解】LibreOJ #6279. 数列分块入门 3
题面
题目描述
给定一个长为 的数列,以及 个操作,操作涉及区间加法,询问区间内小于某个值 的前驱(比其小的最大元素)。
输入格式
第一行输入一个数字 。
第二行输入 个数字,第 个数字为 ,以空格隔开。
接下来输入 行询问,每行输入四个数字 ,以空格隔开。
若 ,表示将位于 的之间的数字都加 。
若 ,表示询问 中 的前驱的值(不存在则输出 )。
输出格式
对于每次询问,输出一行一个数字表示答案。
样例(很水)
Input
41 2 2 30 1 3 11 1 4 40 1 2 21 1 2 4Output
3-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没变(即没找到),则输出-1else write(maxx); //否则输出maxx(前驱)代码
这次的代码是LibreOJ格式化过的,总觉得有亿点奇怪
#include <iostream>#include <algorithm> //sort()要用#include <cstdio>#include <cmath> //sqrt()y要用#define int long longusing 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('\n'); 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;} 【题解】LibreOJ #6279. 数列分块入门 3
https://xn--24wq0n.top/posts/2021-04-05-题解_libreoj_6279_数列分块入门_3/