前言

对一个 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;
}

题目

https://loj.ac/p/6281

题解

将 \(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的更多相关文章

  1. LibreOJ 6281 数列分块入门5

    题目链接:https://loj.ac/problem/6281 参考博客:https://blog.csdn.net/qq_36038511/article/details/79725027 我一开 ...

  2. LibreOJ 6281 数列分块入门 5(分块区间开方区间求和)

    题解:区间开方emmm,这马上让我想起了当时写线段树的时候,很显然,对于一个在2^31次方以内的数,开方7-8次就差不多变成一了,所以我们对于每次开方,如果块中的所有数都为一了,那么开方也没有必要了. ...

  3. LOJ #6281. 数列分块入门 5-分块(区间开方、区间求和)

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

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

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

  5. LOJ 6281 数列分块入门 5

    简化版题意 给出一个长为n的数列,以及n个操作,操作涉及区间开方(每个数都向下取整),区间求和,保证所有数都为有符号32位正整数. N<=50000 Solution 首先我们先思考: 一个有符 ...

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

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

  7. [Libre 6281] 数列分块入门 5 (分块)

    水一道入门分块qwq 题面:传送门 开方基本暴力.. 如果某一个区间全部都开成1或0就打上标记全部跳过就行了 因为一个数开上个四五六次就是1了所以复杂度能过233~ code: //By Menteu ...

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

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

  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. flink 大批量任务提交 yarn 失败问题

    问题现象 用户迁移到新集群后,反馈他们开发平台大量 flink 任务提交失败了,当时集群的 yarn 资源是足够的 排查过程 用户是在他们的开发平台上提交的,查看他们失败的任务,发现是他们提交端主动 ...

  2. [namespace hdk] Balanced_tree 整合

    代码 #include<bits/stdc++.h> using namespace std; namespace hdk{ namespace balanced_tree{ const ...

  3. Docker数据共享与持久化(六)

    接下来介绍如何在 Docker 内部以及容器之间管理数据,在容器中管理数据主要有两种方式: 数据卷(Data Volumes) 挂载主机目录 (Bind mounts) 一.数据卷 数据卷是一个可供一 ...

  4. AD域下,普通用户环境下安装软件需要管理员权限的解决办法

    原理:将AD域普通用户添加到管理组中,使其拥有管理员权限 做法: 1.切换到AD域管理员账户: 2.计算机 -> 管理 -> 用户和组 -> 组 -> Administrato ...

  5. kali系统安装和CVE-2017-12615测试

    1 安装kali系统 1.1 下载VMware压缩包 kali-linux-2022.1 默认的用户和密码是kali 1.2 初始化系统 sudo apt update -y #kali sudo a ...

  6. element表单校验 【登录】

    async btnOK () { // 1. 表单校验 (调用表单的校验方法获取校验结果) // this.$refs.formRef.validate(function (valid) { // c ...

  7. Android复习(六)核心组件—>Activity 简介、生命周期、状态变更

    1. 可以在activity上声明权限,来保证只在进入某个activity是否有某种权限 <manifest> <activity android:name="....&q ...

  8. 为重复使用的HttpClient对象动态修改Timeout

    最近博客园被**了, 赶紧水一文支持一下博客园,加油! 问题现象 当HttpClient被使用过之后, 在修改它们的属性会抛出错误This instance has already started o ...

  9. python+ffmpeg视频转码转格式

    本文转发来自:https://blog.csdn.net/KH_FC/article/details/115771126 废话 python目前自己也是在学习当中,对python也不是特别精通,写视频 ...

  10. dirseach目录扫描工具-安装详细教程

    安装: 1.github源码下载解压 使用 git 安装: 推荐git clone https://github.com/maurosoria/dirsearch.git --depth 1 zip文 ...