分块大法

本文介绍了分块技术在处理区间查询与修改问题中的作用,通过实例展示了如何利用分块来优化算法的效率。文章通过三个问题(小Z的课堂检测、小M的简单题、Hzwer男神的第八题)详细阐述了分块思想的运用,并提供了简单的思路和代码实现。每个问题中,作者都分析了如何通过分块减少暴力遍历的次数,以达到时间和空间复杂度的优化。

所谓分块,就是将原序列处理成各个小块,目的是尽量地达到处理和询问之间的平衡对于分块类问题,常常可以提取出“在给定区间内进行操作,或询问区间内满足给定条件的元素等”。接下来,例题。

首先是男神hzwer的博客链接 hzwer
因为下述题目皆由此改变。

问题 A: 小Z的课堂检测
时间限制: 1 Sec
内存限制: 128 MB
题目描述
大家都知道小Z的课总是十分快的(鬼知道为什么),然后我们阿M同学总是在上课时处于神游状态亦或是休眠状态,所以她对小Z到底讲了什么是一无所知。然而,小Z总是很坏地打断阿M的休眠状态,并问她问题。作为阿M的开黑好伙伴,你当然不希望阿M同学翻车(不然下一个回答问题的人就是你啦)。所以你需要编写个程序帮助阿M求小Z对于知识点到底讲的档次有多深。已知小Z在课上总会扯到涉及到N个知识点,小Z会进行M个动作(讲课或是提问阿M)。由于小Z比较古灵精怪,所以小Z的讲课时只会讲连续的知识点,并且对于这段区间内的知识点都提升一样的档次。而且,小Z也比较懒,所以小Z只会问阿M关于某一个知识点的了解程度。
输入
第一行读入N,表示小Z要涉及到N个知识点
第二行读入A[1],A[2]……A[N-1],A[N]表示小Z上几节课已经把第i个知识点的 难度提升到A[i]的难度
第三行读入M,表示小Z要进行M个动作
接下来M行,读入Choice
若Choice=1,则表示小Z要讲课啦
接下来读入L,R,X 表示小Z要对L到R这些连续的知识点提升难度X
若Choice=2,则表示小Z要提问啦
接下来读入K,表示小Z问阿M第K个知识点他已经讲到哪个难度了
输出
每行输出一个数表示阿M应该回答的正确答案
样例输入
10
1 2 3 4 5 6 7 8 9 10
5
1 2 3 4
2 3
1 3 4 5
2 5
1 5 8 5

7
5 3 7 7 5 8 5
9
1 2 7 -1
2 1
2 2
1 2 3 1
1 2 7 2
2 2
1 3 3 -1
2 3
2 1
样例输出
7
5

5
2
5
8
5
数据范围
对于50%的数据,N<=1000,M<=1000
对于100%的数据,N<=100000,M<=100000 |X|<=50000
|A[i]|<=50000;

简单思路:
首先是最暴力的思路,对于每次修改暴力for一遍,我们不难发现,这样做,并不是所有的修改在后面的过程中都会被询问到,所以我们考虑分块。
我们假设序列长n,每块大小为m,所以共有n/m个块。
对于每段修改[l,r],同样分块后,最多处理得到n/m个完整的块,和左右两段不完整的块,图示如下:这里写图片描述
所以每次修改的时间复杂度为O(n/m+m),由基本不等式,易得,当n/m=m=sqrt(n)时,我们得到的块的大小是比较合理的。
对于查询x,ans=a[x]+tag[bl[x]]。

代码实现:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll tag[500005],bl[500005],a[500005];
ll cho,n,m,del,l,r,x,blo;
void deal(ll l,ll r,ll del)
{
    for (int i=l;i<=min(r,blo*bl[l]);i++) a[i]+=del;
    if (bl[l]!=bl[r]) 
     for (int i=(bl[r]-1)*blo+1;i<=r;i++) a[i]+=del;
    for (int i=bl[l]+1;i<=bl[r]-1;i++) tag[i]+=del; 
}
ll search(ll x)
{
    return (a[x]+tag[bl[
### 分块法的概念与实现 #### 什么是分块法? 分块法是一种通过将原始数据划分为若干个小块,并对每一块进行预处理的技术,目的是优化查询或其他操作的效率。这种方法的核心在于减少全局范围内的遍历次数,转而在局部范围内完成更多工作[^4]。 #### 分块法的应用场景 分块法适用于以下情况: - 原始数据量较大,直接暴力求解时间复杂度过高。 - 数据可以被合理地划分为多个独立的小块,且这些小块能够支持快速访问或计算。 #### 分块查找的具体实现 以下是基于 C 语言的一个典型分块查找算法实现: ```c #include <stdio.h> #define N 10 // 总元素数量 #define B 3 // 每个子块大小 // 初始化数组和索引表 int data[N] = {5, 12, 17, 25, 30, 35, 40, 45, 50, 60}; int index_table[B] = {data[2], data[5], data[8]}; // 子块的最大值作为索引表 // 查找函数 int block_search(int key) { int i; // 首先定位到目标可能存在的子块 for (i = 0; i < B && key >= index_table[i]; ++i); // 计算该子块起始位置并顺序查找 if (i != 0) --i; // 调整回最后一个满足条件的子块 int start = i * (N / B), end = ((i + 1) * (N / B)) - 1; for (int j = start; j <= end; ++j) { if (data[j] == key) return j; // 找到返回索引 } return -1; // 未找到返回 -1 } int main() { int target = 35; int result = block_search(target); printf("Key %d found at position: %d\n", target, result); return 0; } ``` 上述代码展示了如何利用分块技术加速查找过程。程序首先构建了一个索引表 `index_table` 来存储各子块中的最大值,随后在实际查找过程中,先通过比较缩小搜索范围至某个特定子块,再对该子块内部逐一扫描以确认是否存在目标键值[^5]。 #### LU 分解中的分块应用 对于大规模矩阵而言,传统的 LU 分解可能会因为缓存缺失而导致性能下降。为了缓解这一问题,可以通过引入分块策略重构整个分解流程。具体做法是把大矩阵分割成较小尺寸的子矩阵(即“块”),并对它们单独施加标准 LU 方法直至覆盖全部区域为止[^1]。 例如,在 MATLAB 和 OpenCV 这样的高效数值库中均采用了类似的思路来提升运行速度以及硬件资源利用率。值得注意的是,这种调整并非单纯修改现有源码即可达成目的;而是需要依据底层原理重新规划逻辑框架以便充分发挥现代处理器架构的优势特性。 --- ###
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值