【分块】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 这里如果用数组的话元 ...
随机推荐
- Git 客户端基本使用及新手常见问题
Git作为一个版本管理工具,在企业中的应用越来越普遍.作为一个测试工程师,不可避免会需要接触到Git的相关操作,以下整理Git客户端的常见操作,以及应用中新手常碰到的一些问题. 1.环境安装及配置 G ...
- Sketch Measure切图插件无法导出标注 (换插件绕过解决)
环境 MacOS 10.15+ sketch版本 69.2 插件版本 2.8.1 遇到的问题 导出标注卡死 更新版本无解,到插件的github issue中找到解决方法 使用MeaXure插件,git ...
- Serilog文档翻译系列(五) - 编写日志事件
日志事件通过 Log 静态类或 ILogger 接口上的方法写入接收器.下面的示例将使用 Log 以便语法简洁,但下面显示的方法同样可用于接口. Log.Warning("Disk quot ...
- Nuxt.js 应用中的 app:rendered 钩子详解
title: Nuxt.js 应用中的 app:rendered 钩子详解 date: 2024/10/2 updated: 2024/10/2 author: cmdragon excerpt: 摘 ...
- 数组 Array 的属性 和 方法总结
1. Array 的属性 2. Array 的方法 2.1 增加数组单元 参数一半都是数组单元 a)unshift 方法 在数组的最前面添加数组元素 <script> const arr ...
- 14 Positional Encoding (为什么 Self-Attention 需要位置编码)
博客配套视频链接: https://space.bilibili.com/383551518?spm_id_from=333.1007.0.0 b 站直接看 配套 github 链接:https:// ...
- 程序员开发利器:Your Commands网站上线
程序员开发利器:Your Commands网站上线 先上链接: https://www.ycmds.cc 背景 各种命令行工具是我们IT行业日常工作离不开的,但是对于命令行工具的使用有一个痛点:文档上 ...
- KubeSphere 边缘节点 IP 冲突的分析和解决思路分享
在上一篇监控问题排查的文章中,笔者分析了 KubeSphere 3.1.0 集成 KubeEdge 中的边缘监控原理和问题排查思路,在介绍 EdgeWatcher 组件时提到了"边缘节点的内 ...
- Spring中的事务提交事件
如果想在spring操作事务结束后执行一些代码,应该怎么办? 为什么要这样?比如我们在事务中给其他系统发了消息,期望事务提交后过一会收到这个系统的回应,然后操作刚刚提交的数据.但是如果回应来的太快就像 ...
- thinkphp5 模型批量增加数据小记
楼主最近在学习thinkphp5,真的没应广大使用教程所说:你最好就是没学过thinkphp3.2.要不然苦恼重重. 因为想将一些功能实现一次,故自己写了一个文件上传类. 可以实现单文件,多文件上传( ...