【ybt金牌导航6-3-2】区间计数(分块)(二分)

区间计数

题目链接:ybt金牌导航6-3-2

题目大意

给你一个数组,要你支持几个操作:
区间加值和查询一个区间有多少个大于等于某个值的数。

思路

我们考虑用分块来做。
区间加值很容易,暴力加和整块打标记就行。
那接着问题是查询,维护区间有多少大于等于某个值的数看起来不现实。
那我们考虑搞点东西,让它可以比较好的求。不难想到可以将区间内数排序,然后询问就二分。
那你区间修改的时候整体修改不用重新排序,单独暴力修改的才要排序。
那修改和询问都只是加了个 logn,复杂度仍然可以接受。
那就可以了。

代码

#include<cmath> #include<cstdio> #include<algorithm> using namespace std; int n, q, a[1000002], sa[1000002]; int S, s, bl[1002], br[1002]; int lz[1002], x, y, z; char c; int read() { int re = 0, zf = 1; c = getchar(); while (c < '0' || c > '9') { if (c == '-') zf = -zf; c = getchar(); } while (c >= '0' && c <= '9') { re = (re << 3) + (re << 1) + c - '0'; c = getchar(); } return re * zf; } void blocksort(int now) {//给这个块排序 for (int i = bl[now]; i <= br[now]; i++) sa[i] = a[i]; sort(sa + bl[now], sa + br[now] + 1); } void premake() {//一开始搞好每个块 for (int i = 1; i <= n; i++) if (i % S == 1) { br[s] = i - 1; bl[++s] = i; } br[s] = n; bl[s + 1] = br[s + 1] = n + 1; for (int i = 1; i <= s; i++) blocksort(i); } void add_val(int l, int r, int val) { int L = (l - 1) / S + 1, R = (r - 1) / S + 1; if (r - l + 1 <= 2 * S) {//直接暴力处理 for (int i = l; i <= r; i++) a[i] += val; for (int i = L; i <= R; i++) blocksort(i); return ; } bool loneyl = 1, loneyr = 1; if (l == bl[L]) loneyl = 0, L--;//可能没有需要暴力处理的的 if (r == br[R]) loneyr = 0, R++; for (int i = L + 1; i < R; i++)//整块的 lz[i] += val; for (int i = l; i <= br[L]; i++)//两边暴力 a[i] += val; if (loneyl) blocksort(L); for (int i = bl[R]; i <= r; i++) a[i] += val; if (loneyr) blocksort(R); } int find(int b, int lim) {//二分找第一个大于等于的位置(然后得到大于等于的个数) int l = bl[b], r = br[b], ans = br[b] + 1; while (l <= r) { int mid = (l + r) >> 1; if (sa[mid] >= lim) { ans = mid; r = mid - 1; } else l = mid + 1; } return br[b] - ans + 1; } int query(int l, int r, int lim) { int L = (l - 1) / S + 1, R = (r - 1) / S + 1, re = 0; if (r - l + 1 <= 2 * S) {//暴力处理 for (int i = l; i <= r; i++) if (a[i] + lz[(i - 1) / S + 1] >= lim) re++; return re; } if (l == bl[L]) L--; if (r == br[R]) R++; for (int i = L + 1; i < R; i++) re += find(i, lim - lz[i]);//整块 for (int i = l; i <= br[L]; i++)//两边 if (a[i] + lz[L] >= lim) re++; for (int i = bl[R]; i <= r; i++) if (a[i] + lz[R] >= lim) re++; return re; } int main() { n = read(); q = read(); for (int i = 1; i <= n; i++) a[i] = read(); S = sqrt(n); premake(); while (q--) { c = getchar(); while (c != 'M' && c != 'A') c = getchar(); if (c == 'M') { x = read(); y = read(); z = read(); add_val(x, y, z); continue; } if (c == 'A') { x = read(); y = read(); z = read(); printf("%d\n", query(x, y, z)); continue; } } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_6-3-2.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(54)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示