前言

对一个 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. 邀请你参与字节跳动 UME 插件开发竞赛

    UME 是由字节跳动 Flutter Infra 团队出品和维护的 Flutter 应用内调试工具.通过在 Flutter 应用中加入 UME 工具,开发者们可以直接在应用内查看调试信息,而无需使用 ...

  2. 第15天:信息打点-主机架构&蜜罐识别&WAF识别&端口扫描&协议识别&服务安全

    端口扫描:Nmap.Masscan.网络空间 开放状态:Close Open Filtered https://nmap.org/download.html https://github.com/ro ...

  3. 2How To Use Python On A Web Page With Jinja2 - Fla 21:30

    # localhost:5000/user/John @app.route("/user/<name>") def user(name): return render_ ...

  4. Java日期时间API系列20-----Jdk8中java.time包中的新的日期时间API类,ZoneId时区ID大全等。

    Java日期时间API系列19-----Jdk8中java.time包中的新的日期时间API类,ZonedDateTime与ZoneId和LocalDateTime的关系,ZonedDateTime格 ...

  5. 如何让img图片居中

    说明:img是行内块元素,用一个盒子(父元素)嵌套img(子元素) text-align:center;可以让父元素为块元素的行内块或行内元素水平居中: vaertical-align:middle; ...

  6. 封神台 SQL注入 靶场 (猫舍)手动注入

    封神台 SQL注入 靶场 (猫舍)手动注入 靶场地址 http://pu2lh35s.ia.aqlab.cn/?id=1 使用脚本 可以直接使用sqlmap脚本 直接 对这个地址进行测试 不过这样实在 ...

  7. SQL注入利用及绕过总结

    SQL注入及绕过姿势总结 概述 SQL注入指用户输入的参数可控且没有被过滤,攻击者输入的恶意代码被传到后端与SQL语句一起构造并在数据库中执行 不同数据库的语法可能存在差异,以MySQL为例,其他差异 ...

  8. DIY Matter Bridge 和智能锁简单联动的实践

    一. 写在前面 在之前的博客文章 <基于乐鑫 ESP32-C3 的 Matter Light 实践>中,我们利用乐鑫的硬件和 SDK 方案实现了简单的 Light 例程,并对 Matter ...

  9. 某物联网数智化园区行业基于 KubeSphere 的云原生实践

    公司简介 作为物联网 + 数智化园区一体化解决方案提供商,我们致力于为大中型园区.停车场提供软硬件平台,帮助园区运营者实现数字化.智能化运营. 在使用 K8s 之前我们使用传统的方式部署上线,使用 s ...

  10. SQL Server创建用户只能访问指定数据库和视图

    我们在给数据库用户赋予权限时,有时候不想让该用户看到太多过程表和过程视图,这时就需要限定用户的访问权限 第一步:创建用户 创建数据库连接后,进入安全性--登录名,单击右键,新建登录名,并设置默认数据库 ...