题目

https://loj.ac/p/6279

题解

将 \(n\) 个元素的数组 \(a\) 按块长 \(\sqrt{n}\) 进行分块处理。为每个块设置一个懒添加标记 \(add[i]\),代表这个区间每个元素共同添加的数值大小。

对于任意一个无序数组,想要维护出该数组内某个值的前驱(即小于某个值的最大元素),时间复杂度都将来到 \(O(n)\);对于任意一个有序数组,想要维护出该数组内某个值的前驱,可以使用时间复杂度为 \(O(logn)\) 的二分法来维护。

对于每个块,都将数据拷贝到一个备份数组,随后将备份数组进行排序,单个块该操作时间复杂度为 \(O(\sqrt{n} + \sqrt{n}log\sqrt{n})\)。

若查询覆盖块 \(i\) 的全部元素,那么只需要对块 \(i\) 的备份数组进行二分查找出 \(c - add[i]\) 的前驱,单次操作时间复杂度 \(O(log\sqrt{n})\),此类块至多只有 \(\sqrt{n}\) 块,最差时间复杂度 \(O(\sqrt{n}log\sqrt{n})\);若查询未覆盖块 \(i\) 的全部元素,那么可以暴力维护出 \(c - add[i]\) 在原数列中的前驱,单次操作时间复杂度 \(O(\sqrt{n})\),此类块至多只有 \(2\) 块,最差时间复杂度 \(O(2\sqrt{n})\)。

对于区间加操作,将添加值存储在符合整块都进行加法操作的块的懒标记 \(add[i]\) 上,单次操作时间复杂度 \(O(1)\),此类块至多只有 \(\sqrt{n}\) 块,最差时间复杂度 \(O(\sqrt{n})\);未符合整块的进行加法操作则进行暴力处理,但是执行完加法后会破坏备份数组的有序性,需要重新备份且排序,单次操作时间复杂度 \(O(2\sqrt{n} + \sqrt{n}log\sqrt{n})\),此类块至多只有 \(2\) 块,最差时间复杂度 \(O(4\sqrt{n} + 2\sqrt{n}log\sqrt{n})\)。

参考代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long; constexpr ll INF = -1e18;
int n, op, l, r;
int len;//块长
ll c;
ll a[100005];//数列
ll b[100005];//备份数组
ll add[320];//懒添加标记
int lidx[320];//块的左下标
int ridx[320];//块的右下标 /*初始化块*/
void initPieces() {
len = sqrt(n);
memcpy(b + 1, a + 1, sizeof(ll) * n);
for (int i = 1, j = 1; i <= n; i += len, ++ j) {
lidx[j] = i;//左闭
ridx[j] = min(i + len, n + 1);//右开
sort(b + lidx[j], b + ridx[j]);
}
} /*获取下标 x 所在的块的索引*/
int getPieceId(int x) {
return (x - 1) / len + 1;
} /*判断下标 x 是否为块的左边界*/
bool isLeftBoundary(int x) {
return (x - 1) % len == 0;
} /*判断下标 x 是否为块的右边界*/
bool isRightBoundary(int x) {
return x % len == 0;
} int main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; ++ i) cin >> a[i];
initPieces();
for (int i = 0; i < n; ++ i) {
cin >> op >> l >> r >> c;
bool isLe = isLeftBoundary(l), isRi = isRightBoundary(r);
int le = getPieceId(l), ri = getPieceId(r);
if (op) {
ll res = INF;//用 res 维护前驱元素的值
for (int i = isLe ? le : le + 1, j = isRi ? ri : ri - 1; i <= j; ++ i) {
int loc = lower_bound(b + lidx[i], b + ridx[i], c - add[i]) - b - 1;//二分维护大于等于 c - add[i] 的第一个元素的下标位置
if (b[loc] == c - add[i]) -- loc;//维护 c - add[i] 的前驱的下标
if (loc >= lidx[i]) {//若下标位于当前块的范围内,则更新前驱
res = max(res, b[loc] + add[i]);//维护前驱
}
}
if (!isLe) {
while (l <= r) {
if (a[l] + add[le] < c) res = max(res, a[l] + add[le]);
if (isRightBoundary(l)) break;
++ l;
}
}
if (!isRi) {
while (l <= r) {
if (a[r] + add[ri] < c) res = max(res, a[r] + add[ri]);
if (isLeftBoundary(r)) break;
-- r;
}
}
if (res == INF) res = -1;
cout << res << '\n';
} else {
for (int i = isLe ? le : le + 1, j = isRi ? ri : ri - 1; i <= j; ++ i) add[i] += c;
if (!isLe) {
while (l <= r) {
a[l] += c;
if (isRightBoundary(l)) break;
++ l;
}
//重构第 le 块的备份数组
memcpy(b + lidx[le], a + lidx[le], sizeof(ll) * (ridx[le] - lidx[le]));
sort(b + lidx[le], b + ridx[le]);
}
if (!isRi) {
while (l <= r) {
a[r] += c;
if (isLeftBoundary(r)) break;
-- r;
}
//重构第 ri 块的备份数组
memcpy(b + lidx[ri], a + lidx[ri], sizeof(ll) * (ridx[ri] - lidx[ri]));
sort(b + lidx[ri], b + ridx[ri]);
}
}
}
return 0;
}

【分块】LibreOJ 6279 数列分块入门3的更多相关文章

  1. LibreOj 6279数列分块入门 3 练习了一下set

    题目链接:https://loj.ac/problem/6279 推荐博客:https://blog.csdn.net/qq_36038511/article/details/79725027 这题区 ...

  2. LibreOJ 6279 数列分块入门 3(分块+排序)

    题解:自然是先分一波块,把同一个块中的所有数字压到一个vector中,将每一个vector进行排序.然后对于每一次区间加,不完整的块加好后暴力重构,完整的块直接修改标记.查询时不完整的块暴力找最接近x ...

  3. LOJ #6279. 数列分块入门 3-分块(区间加法、查询区间内小于某个值x的前驱(比其小的最大元素))

    #6279. 数列分块入门 3 内存限制:256 MiB时间限制:1500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: hzwer 提交提交记录统计测试数据讨论 3   题目描述 给 ...

  4. loj 6278 6279 数列分块入门 2 3

    参考:「分块」数列分块入门1 – 9 by hzwer 2 Description 给出一个长为\(n\)的数列,以及\(n\)个操作,操作涉及区间加法,询问区间内小于某个值\(x\)的元素个数. 思 ...

  5. LibreOJ 6277. 数列分块入门 1 题解

    题目链接:https://loj.ac/problem/6277 题目描述 给出一个长为 \(n\) 的数列,以及 \(n\) 个操作,操作涉及区间加法,单点查值. 输入格式 第一行输入一个数字 \( ...

  6. LibreOJ 6277 数列分块入门 1(分块)

    题解:感谢hzwer学长和loj让本蒟蒻能够找到如此合适的入门题做. 这是一道非常标准的分块模板题,本来用打标记的线段树不知道要写多少行,但是分块只有这么几行,极其高妙. 代码如下: #include ...

  7. LibreOJ 6278. 数列分块入门 2 题解

    题目链接:https://loj.ac/problem/6278 题目描述 给出一个长为 \(n\) 的数列,以及 \(n\) 个操作,操作涉及区间加法,询问区间内小于某个值 \(x\) 的元素个数. ...

  8. LOJ 6279 数列分块入门3

    嗯... 题目链接:https://loj.ac/problem/6279 这道题在分块的基础上用vc数组记录,然后最后分三块,两边暴力枚举找前驱,中间lower_bound找前驱. AC代码: #i ...

  9. LibreOJ 6285. 数列分块入门 9

    题目链接:https://loj.ac/problem/6285 其实一看到是离线,我就想用莫队算法来做,对所有询问进行分块,但是左右边界移动的时候,不会同时更新数字最多的数,只是后面线性的扫了一遍, ...

  10. LibreOJ 6282. 数列分块入门 6

    题目链接:https://loj.ac/problem/6282 参考博客:http://www.cnblogs.com/stxy-ferryman/p/8560551.html 这里如果用数组的话元 ...

随机推荐

  1. Log4j2—漏洞分析(CVE-2021-44228)

    目录 Log4j2漏洞原理 漏洞根因 调用链源码分析 调用链总结 漏洞复现 dns rmi Log4j2漏洞原理 前排提醒:本篇文章基于我另外一篇总结的JNDI注入后写的,建议先看该文章进行简单了解J ...

  2. STL标准模板库

    STL(Standard Template Library)标准模板库 是C++标准库中的一个重要组成部分,它提供了一组通用的模板类和函数,用于数据结构和算法的实现.STL的核心部分包括容器.算法和迭 ...

  3. USB协议详解第9讲(USB描述符-HID描述符)

    1.HID设备概述 USB设备中有一大类就是HID设备,即Human Interface Devices,人机接口设备.这类设备包括鼠标.键盘.游戏手柄等,主要用于人与计算机进行交互.HID设备可以作 ...

  4. Nodejs C++插件(N-API)

    Nodejs C++插件(N-API) 0. 环境搭建 1. JS中调用C++方法 1.1 JS中调用源文件的C++方法 1.2 JS中调用动态库的C++方法 2. C++中调用JS方法 2.1. C ...

  5. 我们在 vue 项目中如何做路由导航守卫

    一般在 src 文件夹新建一个 permission 文件 ,单独用来做路由导航守卫业务 ,在 main.js 导入文件即可 : 主要功能有 判断是否有 token ,以此判断用户是不是登录了 :

  6. Blazor Hybrid 实战体验:那些你可能没预料到的坑没预料到的坑

    前言 昨天写了一篇介绍 Blazor Hybrid 技术的文章,但限于篇幅,一些问题未能深入探讨.今天,我想继续记录使用 Blazor Hybrid 过程中遇到的几个问题,以及这个技术目前的一些局限性 ...

  7. MYSQL存储过程-练习4 loop循环

    MYSQL存储过程-练习4 loop循环 创建存储过程 1 DELIMITER $$ 2 3 CREATE PROCEDURE `sp_loop`() 4 BEGIN 5 DECLARE i INT; ...

  8. TextIn ParseX文档解析SDK工具新增Java版本

    TextIn ParseX通用文档解析是一款大模型友好的解析工具,支持将pdf文档.jpg.img图像等文件快速转换为markdown格式,支持各类表格.公式解析,帮助大语言模型的数据清洗和文档问答任 ...

  9. base64编码图片二进制数据后直接保存在html文件中

    相关内容: 在markdown编辑器中嵌入base64图片 看到一个帖子,那就是base64编码用来http服务中对二进制文件编码,那么可以不可以直接在html文件中使用base64编码后的字符串来表 ...

  10. 模算术 modular arithmetic

    https://en.wikipedia.org/wiki/Modular_arithmetic#Integers_modulo_n 模算术: 整数达到特定值时会' 折返 ' 回来-- 模数 modu ...