【分块】LibreOJ 6278 数列分块入门2
题目
题解
将 \(n\) 个元素的数组 \(a\) 按块长 \(\sqrt{n}\) 进行分块处理。为每个块设置一个懒添加标记 \(add[i]\),代表这个区间每个元素共同添加的数值大小。
对于任意一个无序数组,想要维护出该数组内小于某个值的元素个数,时间复杂度都将来到 \(O(n)\);对于任意一个有序数组,想要维护出该数组内小于某个值的元素个数,可以使用时间复杂度为 \(O(logn)\) 的二分法来维护。
对于每个块,都将数据拷贝到一个备份数组,随后将备份数组进行排序,单个块该操作时间复杂度为 \(O(\sqrt{n} + \sqrt{n}log\sqrt{n})\)。
若查询覆盖块 \(i\) 的全部元素,那么只需要对块 \(i\) 的备份数组进行二分查找出小于等于 \(c^2 - add[i]\) 的元素个数,单次操作时间复杂度 \(O(log\sqrt{n})\),此类块至多只有 \(\sqrt{n}\) 块,最差时间复杂度 \(O(\sqrt{n}log\sqrt{n})\);若查询未覆盖块 \(i\) 的全部元素,那么可以暴力维护出原数列小于等于 \(c^2 - 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;
int n;//数列元素个数
int op, l, r;
int len;//块长
ll c;
ll a[50005];//数列
ll b[50005];//排序后的数列
ll add[230];//懒添加标记
int lidx[230];//块的左下标
int ridx[230];//块的右下标
/*初始化块*/
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) {
int res = 0;
c = c * c;
for (int i = isLe ? le : le + 1, j = isRi ? ri : ri - 1; i <= j; ++ i) {
res += lower_bound(b + lidx[i], b + ridx[i], c - add[i]) - b - lidx[i];
}
if (!isLe) {
while (l <= r) {
res += a[l] + add[le] < c;
if (isRightBoundary(l)) break;
++ l;
}
}
if (!isRi) {
while (l <= r) {
res += a[r] + add[ri] < c;
if (isLeftBoundary(r)) break;
-- r;
}
}
cout << res << '\n';
} else {
for (int i = isLe ? le : le + 1, j = isRi ? ri : ri - 1; i <= j; ++ i) add[i] += c;
if (!isLe) {
//根号 n 时间复杂度重构第 le 块
while (l <= r) {
a[l] += c;
if (isRightBoundary(l)) break;
++ l;
}
memcpy(b + lidx[le], a + lidx[le], sizeof(ll) * (ridx[le] - lidx[le]));
sort(b + lidx[le], b + ridx[le]);
}
if (!isRi) {
//根号 n 时间复杂度重构第 ri 块
while (l <= r) {
a[r] += c;
if (isLeftBoundary(r)) break;
-- r;
}
memcpy(b + lidx[ri], a + lidx[ri], sizeof(ll) * (ridx[ri] - lidx[ri]));
sort(b + lidx[ri], b + ridx[ri]);
}
}
}
return 0;
}
【分块】LibreOJ 6278 数列分块入门2的更多相关文章
- LibreOJ 6278. 数列分块入门 2 题解
题目链接:https://loj.ac/problem/6278 题目描述 给出一个长为 \(n\) 的数列,以及 \(n\) 个操作,操作涉及区间加法,询问区间内小于某个值 \(x\) 的元素个数. ...
- LibreOJ 6278 数列分块入门 2(分块)
题解:非常高妙的分块,每个块对应一个桶,桶内元素全部sort过,加值时,对于零散块O(sqrt(n))暴力修改,然后暴力重构桶.对于大块直接整块加.查询时对于非完整块O(sqrt(n))暴力遍历.对 ...
- LOJ #6278. 数列分块入门 2-分块(区间加法、查询区间内小于某个值x的元素个数)
#6278. 数列分块入门 2 内存限制:256 MiB时间限制:500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: hzwer 提交提交记录统计测试数据讨论 6 题目描述 给出 ...
- #6278. 数列分块入门 2(询问区间内小于某个值 xx 的元素个数)
题目链接:https://loj.ac/problem/6278 题目大意:中文题目 具体思路:数列分块模板题,对于更新的时候,我们通过一个辅助数组来进行,对于原始的数组,我们只是用来加减,然后这个辅 ...
- LibreOJ 6277. 数列分块入门 1 题解
题目链接:https://loj.ac/problem/6277 题目描述 给出一个长为 \(n\) 的数列,以及 \(n\) 个操作,操作涉及区间加法,单点查值. 输入格式 第一行输入一个数字 \( ...
- LibreOJ 6277. 数列分块入门 2
题目链接:https://loj.ac/problem/6278 参考博客:https://blog.csdn.net/qq_36038511/article/details/79725027 这题我 ...
- LibreOJ 6277 数列分块入门 1(分块)
题解:感谢hzwer学长和loj让本蒟蒻能够找到如此合适的入门题做. 这是一道非常标准的分块模板题,本来用打标记的线段树不知道要写多少行,但是分块只有这么几行,极其高妙. 代码如下: #include ...
- LOJ#6278. 数列分块入门 2
在一个区间上进行操作,一种操作是某个小区间都加上c,另一个查找这个区间内大于c*c的数 我们可以另外开一个数组在保存a中的每个分块内的相对值,然后每次对a加值,并把a的值赋给b,不同的是b内的各个分块 ...
- LibreOJ 6285. 数列分块入门 9
题目链接:https://loj.ac/problem/6285 其实一看到是离线,我就想用莫队算法来做,对所有询问进行分块,但是左右边界移动的时候,不会同时更新数字最多的数,只是后面线性的扫了一遍, ...
- LibreOJ 6282. 数列分块入门 6
题目链接:https://loj.ac/problem/6282 参考博客:http://www.cnblogs.com/stxy-ferryman/p/8560551.html 这里如果用数组的话元 ...
随机推荐
- 一个 tomcat 下如何部署多个项目?附详细步骤
一个tomcat下如何部署多个项目?Linux跟windows系统下的步骤都差不多,以下linux系统下部署为例.windows系统下部署同理. 一.不修改端口,部署多个项目 清楚tomcat目录结构 ...
- java_day3_Scanner,顺序结构,选择结构(if,switch),循环结构(for,while),
一.Scanner 键盘录入:程序运行过程中,用户可以根据自己的需求输入参与运算的值 实现键盘录入的步骤 1.导包 2.创建键盘录入对象 3.调用方法实现键盘录入 1)输入整数 2)输入字符串 pub ...
- Cookie——基本使用
Cookie 基本使用 Cookie 原理 Cookie 使用细节 Cookie jsp中获取Cookie
- freemarker实现导出word复选框可点击效果
记一次java导出word文档,导出的word文档里包含复选框并且能点击,一开始做了个输出字符的,比如这样: □,然而并不能满足需求,网上找了一大堆也都是这种的. 正文开始: 先在word中添加复选框 ...
- Python 潮流周刊#70:微软 Excel 中的 Python 正式发布!(摘要)
本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...
- SuperMap iServer新增支持FlatGeobuf数据格式,查询渲染性能提升2-3倍
导语 FlatGeobuf是一种地理数据存储格式,采用了二进制编码,相比其他文本或XML格式更高效,可以显著减小文件大小,这使得数据的传输和存储更加快速和高效. SuperMap iServer 11 ...
- AD域下,普通用户环境下安装软件需要管理员权限的解决办法
原理:将AD域普通用户添加到管理组中,使其拥有管理员权限 做法: 1.切换到AD域管理员账户: 2.计算机 -> 管理 -> 用户和组 -> 组 -> Administrato ...
- USB协议详解第4讲(USB描述符-标准配置描述符)
1.USB描述符 USB描述符有设备描述符.标准配置描述符.接口描述符.端点描述符.字符串描述符,HID设备有HID描述符.报告描述符和物理描述符.今天主要是学习USB标准配置描述符的组成. 2.标准 ...
- iOS关于高德地图定位和热点搜索使用小结
最近项目刚刚忙完,有空整理一下用到的相关技术点.地图是比较常见的功能模块,现在用的比较多的是地图当前位置显示,公交.骑行.步行路线信息,附近热点位置搜索.现在国内用的比较多的是高德.百度,国外的话可以 ...
- Linux 系统常见 的命令
uname -a 查看linux 的版本信息 : pwd :打印当前的工作目录 ,print work directory: cd 改变目录 ,change directory : cd . 当前目 ...