题目

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. USB2.0 的LPM和USB3.0的LPM区别

    USB 2.0 和 USB 3.0 都支持低功耗管理机制(LPM,Link Power Management),但两者的实现方式和目标不同.以下是 USB 2.0 的 LPM 和 USB 3.0 的 ...

  2. 分享几个实用且高效的EF Core扩展类库,提高开发效率!

    前言 今天大姚给大家分享3款开源且实用的EF Core扩展类库,希望能帮助你在使用 EF Core 进行数据库开发变得更加高效和灵活,提高开发效率. EF Core介绍 Entity Framewor ...

  3. 在 Exchange Server 中配置特定于客户端的消息大小限制

    在 Exchange Server 中配置特定于客户端的消息大小限制 微软官方详细文档如下: https://learn.microsoft.com/zh-cn/exchange/architectu ...

  4. 安装nvm管理node版本(npm、yarn)

    安装nvm管理node版本(npm.yarn) 一.下载安装nvm nvm网址:https://nvm.uihtm.com/ 1.点击下载链接下载nvm 2.将下载的压缩包解压,解压后双击安装包,然后 ...

  5. 好上好信息 API 微服务集群在 KubeSphere 的部署实践

    作者:徐鹏.深圳好上好信息(001298).技术副总监.负责云服务器团队的架构设计及业务开发,拥抱云原生,乐于分享,终生学习. 公司简介 好上好信息(001298)是中国大陆一家致力于为中国智造提供全 ...

  6. VueJS实现迷糊查询

    原文请查看公共号 前置条件: 开发环境:windows 开发框架:vue2.5 ,vue-cli 4.0+ 编辑器:HbuilderX 兼容版本:vue2.5     Chrome 99.0.4844 ...

  7. kerberos系列之spark认证配置

    大数据安全系列的其它文章 https://www.cnblogs.com/bainianminguo/p/12548076.html-----------安装kerberos https://www. ...

  8. 温习 SPI 机制 (Java SPI 、Spring SPI、Dubbo SPI)

    SPI 全称为 Service Provider Interface,是一种服务发现机制. SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类.这样可以在运行时 ...

  9. spring下 -spring整体架构,JdbcTemplate笔记

    2,搭建Java Maven项目 我的idea是2024.1.1版本,创建普通Maven项目如下图: 用的jdk8,项目名可以自己改,Archetype选图中的第一个就行,之后点 create. 创建 ...

  10. Windows 多次制作母盘,备份文件变大的问题

    公司产品基于Win11 23H2镜像版本制作母盘,我们发现随着版本迭代,基于上一版本母盘生成新母盘备份,母盘文件会越来越大. 此处说明下镜像与母盘文件的区别, 1. 镜像是指操作系统的压缩文件,常见格 ...