【分块】LibreOJ 6281 数列分块入门5
前言
对一个 int 类型的非负整数进行开方下取整,最多只会开方四次大小就不会再发生变化。一个大于 \(0\) 的正整数开方下取整最后的结果比如是 \(1\),而 \(1\) 开方的结果仍然会是 \(1\);\(0\) 开方的结果仍是 \(0\)。
验证int类型整数最多可以开方的次数的demo
#include<bits/stdc++.h>
using namespace std;
int main() {
int a = 2147483647;
for (int i = 0; i < 32; ++ i) {
cout << a << endl;
a = sqrt(a);
if (a == 1) {
cout << "i = " << i;
break;
}
}
return 0;
}
题目
题解
将 \(n\) 个元素的数组 \(a\) 按块长 \(\sqrt{n}\) 进行分块处理。为每个块设置开方懒标记 \(sq[i]\),代表这个区间每个元素共同开方的次数,并且用 \(flag[i]\) 标记 \(sq[i]\) 是否发生变动。同时为每个块设置 \(cnt[i]\) 和 \(sum[i]\),分别代表这个块上开方后大小会变化的元素个数和这个块的所有元素之和。而每个数最多只会开方 \(4\) 次,不妨直接用 \(a[i][j]\) 来维护第 \(i\) 个数开方 \(j\) 次后的值,同时用 \(b[i]\) 维护当前已经开方的次数(亦即 \(a[i][j]\) 中 \(j\) 的值)。
对于区间开方操作,对于整个块的元素都进行开方操作的块判断是否仍存在开方后会变小的元素(即 \(cnt[i] > 0\) 是否成立),若存在则将懒开方标记 \(sq[i] = sq[i] + 1\),并且设置 \(flag[i] = true\) 来标记 \(sq[i]\) 已发生变动,单次操作时间复杂度 \(O(1)\),此类块至多只有 \(\sqrt{n}\) 块,最差时间复杂度 \(O(\sqrt{n})\);未符合整块的进行开方操作则进行暴力处理,并且更新其属于的块的 \(sum[i]\),单次操作时间复杂度 \(O(\sqrt{n})\),此类块至多只有 \(2\) 块,最差时间复杂度 \(O(2\sqrt{n})\)。
对于区间和操作,对于整个块的元素都需要计算到结果中的块判断这个块区间开方懒标记 \(sq[i]\) 是否发生变动(即 \(flag[i] == false\) 是否成立),若成立说明区间开方懒标记 \(sq[i]\) 未发生变动,因此将覆盖到的整块的 \(sum[i]\) 直接添加到结果中即可,单次操作时间复杂度 \(O(1)\),此类块至多只有 \(\sqrt{n}\) 块,最差时间复杂度 \(O(\sqrt{n})\);若不成立,则需要重构这个块,重新计算 \(sum[i], cnt[i]\),并且重新将 \(flag[i]\) 重新置为 \(false\),单次操作时间复杂度 \(O(\sqrt{n})\),每个块最多只会重构 \(4\) 次,最多重构 \(\sqrt{n}\) 块,最差时间复杂度 \(O(4n)\)。未符合整块的进行区间和操作则进行暴力处理单次操作时间复杂度 \(O(\sqrt{n})\),此类块至多只有 \(2\) 块,最差时间复杂度 \(O(2\sqrt{n})\)。
参考代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
char op;//操作
int l, r, c;
int n;//数列长度
int len;//块长
int a[50005][6];//数列,每个数最多开方5次就不会再变小
int b[50005];//维护数列 a 的第二维的下标
int cnt[230];//标记一个区间上开方还会变小的数的个数
int sq[230];//区间开方次数懒标记
bool flag[230];//标记一个区间开方次数懒标记增加
ll sum[230];//区间和
/*获取下标 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;
}
/*获取第 x 块的左边界*/
int getLeftIdx(int x) {
return (x - 1) * len + 1;
}
/*获取第 x 块的右边界*/
int getRightIdx(int x) {
return min(x * len, n);
}
/*初始化块*/
void initPieces() {
len = sqrt(n);
for (int i = 1, j = 1; i <= n; ++ i) {
sum[j] += a[i][0];
if (a[i][0] > 1) ++ cnt[j];
if (a[i][0]) for (int k = 1; k < 6; ++ k) a[i][k] = sqrt(a[i][k - 1]);
if (isRightBoundary(i)) ++ j;
}
}
/* 计算第 x 块的区间和*/
ll calc(int x) {
if (!flag[x]) return sum[x];
cnt[x] = 0;//重新累计cnt[x]
flag[x] = false;
sum[x] = 0LL;//重新计算sum[x]
for (int i = getLeftIdx(x), j = getRightIdx(x); i <= j; ++ i) {
int bidx = min(5, b[i] + sq[x]);//维护二维的索引,最大只会到 5
sum[x] += a[i][bidx];
if (a[i][bidx] > 1) ++ cnt[x];
}
return sum[x];
}
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][0];
initPieces();
for (int p = 0; p < n; ++ p) {
cin >> op >> l >> r >> c;
int le = getPieceId(l), ri = getPieceId(r);
bool isLe = isLeftBoundary(l), isRi = isRightBoundary(r);
if (op == '0') {//区间开方
for (int i = isLe ? le : le + 1, j = isRi ? ri : ri - 1; i <= j; ++ i) {
if (cnt[i] > 0) {//区间上存在开方后会变小的数
sq[i] ++;
flag[i] = true;
}
}
if (!isLe) {
while (l <= r) {
int bidx1 = min(5, b[l] + sq[le]), bidx2 = min(5, bidx1 + 1);
sum[le] -= a[l][bidx1];
sum[le] += a[l][bidx2];
b[l] = b[l] + 1;
if (a[l][bidx1] > 1 && a[l][bidx2] <= 1) -- cnt[le];
if (isRightBoundary(l)) break;
++ l;
}
}
if (!isRi) {
while (l <= r) {
int bidx1 = min(5, b[r] + sq[ri]), bidx2 = min(5, bidx1 + 1);
sum[ri] -= a[r][bidx1];
sum[ri] += a[r][bidx2];
b[r] = b[r] + 1;
if (a[r][bidx1] > 1 && a[r][bidx2] <= 1) -- cnt[ri];
if (isLeftBoundary(r)) break;
-- r;
}
}
} else {//区间和
ll ans = 0LL;
for (int i = isLe ? le : le + 1, j = isRi ? ri : ri - 1; i <= j; ++ i) ans += calc(i);
if (!isLe) {
while (l <= r) {
ans += a[l][min(5, b[l] + sq[le])];
if (isRightBoundary(l)) break;
++ l;
}
}
if (!isRi) {
while (l <= r) {
ans += a[r][min(5, b[r] + sq[ri])];
if (isLeftBoundary(r)) break;
-- r;
}
}
cout << ans << '\n';
}
}
return 0;
}
【分块】LibreOJ 6281 数列分块入门5的更多相关文章
- LibreOJ 6281 数列分块入门5
题目链接:https://loj.ac/problem/6281 参考博客:https://blog.csdn.net/qq_36038511/article/details/79725027 我一开 ...
- LibreOJ 6281 数列分块入门 5(分块区间开方区间求和)
题解:区间开方emmm,这马上让我想起了当时写线段树的时候,很显然,对于一个在2^31次方以内的数,开方7-8次就差不多变成一了,所以我们对于每次开方,如果块中的所有数都为一了,那么开方也没有必要了. ...
- LOJ #6281. 数列分块入门 5-分块(区间开方、区间求和)
#6281. 数列分块入门 5 内存限制:256 MiB时间限制:500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: hzwer 提交提交记录统计测试数据讨论 5 题目描述 给出 ...
- LibreOJ 6277. 数列分块入门 1 题解
题目链接:https://loj.ac/problem/6277 题目描述 给出一个长为 \(n\) 的数列,以及 \(n\) 个操作,操作涉及区间加法,单点查值. 输入格式 第一行输入一个数字 \( ...
- LOJ 6281 数列分块入门 5
简化版题意 给出一个长为n的数列,以及n个操作,操作涉及区间开方(每个数都向下取整),区间求和,保证所有数都为有符号32位正整数. N<=50000 Solution 首先我们先思考: 一个有符 ...
- LibreOJ 6277 数列分块入门 1(分块)
题解:感谢hzwer学长和loj让本蒟蒻能够找到如此合适的入门题做. 这是一道非常标准的分块模板题,本来用打标记的线段树不知道要写多少行,但是分块只有这么几行,极其高妙. 代码如下: #include ...
- [Libre 6281] 数列分块入门 5 (分块)
水一道入门分块qwq 题面:传送门 开方基本暴力.. 如果某一个区间全部都开成1或0就打上标记全部跳过就行了 因为一个数开上个四五六次就是1了所以复杂度能过233~ code: //By Menteu ...
- LibreOJ 6278. 数列分块入门 2 题解
题目链接:https://loj.ac/problem/6278 题目描述 给出一个长为 \(n\) 的数列,以及 \(n\) 个操作,操作涉及区间加法,询问区间内小于某个值 \(x\) 的元素个数. ...
- 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 这里如果用数组的话元 ...
随机推荐
- USB协议详解第5讲(USB描述符-接口描述符)
1.USB描述符 USB描述符有设备描述符.标准配置描述符.接口描述符.端点描述符.字符串描述符,HID设备有HID描述符.报告描述符和物理描述符.今天主要是学习USB接口描述符的组成. 2.接口描述 ...
- 一些git使用命令,误删本地分支,如何找回
1.新建仓库 git init 2.如果要本地更改文件,需要更新到远端. git status # 查看当前文件状态 git add -A # "git add -A" 命令用于将 ...
- iOS中xib文件维护使用小结
最近一直在做项目维护,由于项目比较大,开发时间比较早,早期的很多页面都是用xib拖拽页面控件.简单的页面还好,详情页面也是拖拽搭建,项目维护成本可想而知.闲言少叙,下面说一下不是特别复杂的xib页面维 ...
- Kali && Debain 防火墙规则
Kali && Debain 防火墙规则 查看防火墙规则 iptables -L -n -v iptables -L -n -v 增加防火墙规则:开放指定的端口 iptables -A ...
- 云原生爱好者周刊:目前 WebAssembly 的最佳应用场景有哪些?
云原生一周动态要闻: Istio 1.11 发布 Facebook.Google.Isovalent.微软和 Netflix 宣布成立 eBPF 基金会 GitHub 工程团队将开发环境迁移到 Cod ...
- Spring中的事务提交事件
如果想在spring操作事务结束后执行一些代码,应该怎么办? 为什么要这样?比如我们在事务中给其他系统发了消息,期望事务提交后过一会收到这个系统的回应,然后操作刚刚提交的数据.但是如果回应来的太快就像 ...
- activiti教程
一.工作流介绍 1.1 概念 工作流(Workflow),就是通过计算机对业务流程自动化执行管理.它主要解决的是"使在多个参与者之间按照某种预定义的规则自动进行传递文档.信息或任务的过程,从 ...
- SpringMvc请求注解@ResponseBody
1.概念 注解 @ResponseBody,使用在控制层(controller)的方法上. 2.作用 作用:将方法的返回值,以特定的格式写入到response的body区域,进而将数据返回给客户端. ...
- 如何正确的使用CMDB?
为什么说CMDB在企业中很难被使用起来?第一,CMDB的数据集成与准确性维护是一大挑战,因为需要确保来自多个异构IT系统的配置信息能够实时.准确地同步与更新.第二,复杂的IT环境和快速变化的业务需求使 ...
- Hadoop习题汇总
目录 选择 单选 多选 判断 填空 简答 选择 单选 查看HDFS系统版本的Shell命令,以下正确的是(). hdfs -ver hdfs version (答案) dfsadmin version ...