首页 > 其他分享 >【分块】LibreOJ 6278 数列分块入门2

【分块】LibreOJ 6278 数列分块入门2

时间:2024-11-25 22:45:28浏览次数:5  
标签:数列 LibreOJ 分块 int 6278 复杂度 sqrt 数组 ll

题目

https://loj.ac/p/6278

题解

将 \(n\) 个元素的数组 \(a\) 按块长 \(\sqrt{n}\) 进行分块处理。为每个块设置一个懒添加标记 \(add[i]\),代表这个区间每个元素共同添加的数值大小。

对于任意一个无序数组,想要维护出该数组内小于某个值的元素个数,时间复杂度都将来到 \(O(n)\);对于任意一个有序数组,想要维护出该数组内小于某个值的元素个数,可以使用时间复杂度为 \(O(logn)\) 的二分法来维护。

对于每个块,都将数据拷贝到一个备份数组,随后将备份数组进行排序,单个块该操作时间复杂度为 \(O(\sqrt{n} + \sqrt{n}log\sqrt{n})\)。

若查询覆盖块 \(i\) 的全部元素,那么只需要对块 \(i\) 的备份数组进行二分查找出小于等于 \(c^2 - add[i]\) 的元素个数,单次操作时间复杂度 \(O(log\sqrt{n})\),此类块至多只有 \(\sqrt{n}\) 块,最差时间复杂度 \(O(\sqrt{n}log\sqrt{n})\);若查询未覆盖块 \(i\) 的全部元素,那么可以暴力维护出原数列小于等于 \(c^2 - add[i]\) 的元素个数,单次操作时间复杂度 \(O(\sqrt{n})\),此类块至多只有 \(2\) 块,最差时间复杂度 \(O(2\sqrt{n})\)。

对于区间加操作,将添加值存储在符合整块都进行加法操作的块的懒标记 \(add[i]\) 上,单次操作时间复杂度 \(O(1)\),此类块至多只有 \(\sqrt{n}\) 块,最差时间复杂度 \(O(\sqrt{n})\);未符合整块都进行加法操作则进行暴力处理,但是执行完加法后会破坏备份数组的有序性,需要重新备份且排序,单次操作时间复杂度 \(O(2\sqrt{n} + \sqrt{n}log\sqrt{n})\),此类块至多只有 \(2\) 块,最差时间复杂度 \(O(4\sqrt{n} + 2\sqrt{n}log\sqrt{n})\)。

参考代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

int n;//数列元素个数
int op, l, r;
int len;//块长
ll c;
ll a[50005];//数列
ll b[50005];//排序后的数列
ll add[230];//懒添加标记
int lidx[230];//块的左下标
int ridx[230];//块的右下标

/*初始化块*/
void initPieces() {
	len = sqrt(n);
	memcpy(b + 1, a + 1, sizeof(ll) * n);//拷贝一份原数列
	for (int i = 1, j = 1; i <= n; i += len, ++ j) {
		lidx[j] = i;//左闭
		ridx[j] = min(i + len, n + 1);//右开
		sort(b + lidx[j], b + ridx[j]);//为当前块排序
	}
}

/*获取下标 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;
}

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];
	initPieces();
	for (int i = 0; i < n; ++ i) {
		cin >> op >> l >> r >> c;
		bool isLe = isLeftBoundary(l), isRi = isRightBoundary(r);
		int le = getPieceId(l), ri = getPieceId(r);
		if (op) {
			int res = 0;
			c = c * c;
			for (int i = isLe ? le : le + 1, j = isRi ? ri : ri - 1; i <= j; ++ i) {
				res += lower_bound(b + lidx[i], b + ridx[i], c - add[i]) - b - lidx[i];
			}
			if (!isLe) {
				while (l <= r) {
					res += a[l] + add[le] < c;
					if (isRightBoundary(l)) break;
					++ l;
				}
			}
			if (!isRi) {
				while (l <= r) {
					res += a[r] + add[ri] < c;
					if (isLeftBoundary(r)) break;
					-- r;
				}
			}
			cout << res << '\n';
		} else {
			for (int i = isLe ? le : le + 1, j = isRi ? ri : ri - 1; i <= j; ++ i) add[i] += c;
			if (!isLe) {
                //根号 n 时间复杂度重构第 le 块
				while (l <= r) {
					a[l] += c;
					if (isRightBoundary(l)) break;
					++ l;
				}
				memcpy(b + lidx[le], a + lidx[le], sizeof(ll) * (ridx[le] - lidx[le]));
				sort(b + lidx[le], b + ridx[le]); 
			}
			if (!isRi) {
                //根号 n 时间复杂度重构第 ri 块
				while (l <= r) {
					a[r] += c;
					if (isLeftBoundary(r)) break;
					-- r;
				}
				memcpy(b + lidx[ri], a + lidx[ri], sizeof(ll) * (ridx[ri] - lidx[ri]));
				sort(b + lidx[ri], b + ridx[ri]); 
			}
		}
	}
	return 0;
}

标签:数列,LibreOJ,分块,int,6278,复杂度,sqrt,数组,ll
From: https://www.cnblogs.com/RomanLin/p/18558626

相关文章

  • 【分块】LibreOJ 6279 数列分块入门3
    题目https://loj.ac/p/6279题解将\(n\)个元素的数组\(a\)按块长\(\sqrt{n}\)进行分块处理。为每个块设置一个懒添加标记\(add[i]\),代表这个区间每个元素共同添加的数值大小。对于任意一个无序数组,想要维护出该数组内某个值的前驱(即小于某个值的最大元素),时间复杂度都将......
  • 高性能计算-探究循环分块优化(2-1)
    1.目标:分析循环分块优化技术,并分析cache命中情况假设每个cacheline可以存储b个数据元素。2.源代码分析for(inti=0;i<N;i++){ for(intj=0;j<M;j++) { A[i]+=B[j]; }}cachemiss分析:对A总访问次数为NM,每次访存加载一个cacheline可以加载b个元素,并且连续访问,......
  • 分块
    分块简单理解一下,分块就是优雅的暴力。分块的分类静态分块(只做查询,预处理):静态分块指的是放一些关键点,预处理关键点到关键点的信息,来加速查询的,不能支持修改。目前认为,如果可以离线,静态分块是莫队算法的子集。动态分块(支持修改和查询):动态分块指的是把序列分为一些块,每块......
  • 408数据结构-折半查找,分块查找 自学知识点整理
    前置知识:查找的基本概念折半查找折半查找又称二分查找,它仅适用于有序的顺序表。因个人习惯,下文均用二分代替折半。二分查找的基本思想:首先将给定值ke......
  • 【JS】requestIdleCallback实现分块执行
    点击按钮后,执行一个耗时较长的dom操作,页面很长时间没有响应,给用户一种卡死的现象<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0">&......
  • 张量矩阵乘法分块乘法概述
    张量矩阵乘法分块乘法概述介绍一下矩阵计算相关的内容,从最基本的算法,到Cutlass这些线性代数模版库,特别是Layout代数相关的内容,再逐渐细化到一些硬件实现访存优化和一些算子融合。6.3.1GEMM概述1.GEMM定义对于一个矩阵乘法,定义如下: (6-1)一个矩阵乘法定义,如图6-26......
  • P3863 序列(分块)
    感觉是一个比较厉害的trick,并且从来没见过,记录一下。题意给定\(n\)个数和\(q\)次操作:1lrx:区间\([l,r]\)加\(x\)。2xv:查询在询问之前有多少时刻\(a_x\gev\)。一次操作定义为一个时刻,初始为\(0\)时刻。\(n,q\le10^5\)分析如果\(x\ge0\),那么\(a_i\)的......
  • P3730 曼哈顿交易(经典题,莫队+值域分块)
    记录这一类很典的题目。题意给定\(n\)个数,\(q\)次询问区间\([l,r]\)内出现次数第\(k\)小的数的出现次数。若区间内不同数的个数小于\(k\)输出-1。\(n,q\le10^5\)。分析发现正常的数据结构以及分块都很难维护这种信息,考虑莫队。莫队加入数时,我们需要更新一个数的......
  • 分块式内存管理理论理解
    一,引入             分块式内存管理是一种内存管理策略,它将内存划分为若干个大小相等的块(称为“分区”、“段”或“块”),然后为不同的程序分配这些块。这种策略可以有效地解决内存碎片问题,提高内存利用率。分块式内存管理通常有两种实现方式:固定大小块和可变......
  • 语义分块:改进 AI 信息检索
    RAG系统及其挑战检索增强生成的流行是有充分理由的。它允许AI系统通过结合信息检索和语言生成来回答问题。标准的RAG管道通过摄取数据、检索相关信息并使用它来生成响应来实现这一点。然而,随着数据变得越来越复杂,查询也越来越复杂,传统的RAG系统可能会面临限制。这就是语......