数据结构与算法:分块(一):块状数组、块状链表

发布时间:2026/10/29 10:34:55

数据结构与算法:分块(一):块状数组、块状链表 前言这玩意儿也太妙了一、内容对于 n 长度的数组考虑将其平均划分为块那么就有块数和每个块内的长度都是的级别所以此时就能实现遍历所有块和块内遍历一遍的复杂度都是的了。在维护信息时块内就可以暴力维护整块直接修改标签即可。那么在处理时就只需要对左右散块暴力中间块遍历即可。二、适用场景当区间信息不可合并或者很难合并时那么就无法使用线段树了。而此时往往分块比较容易解决因为块内信息是暴力维护的。除此之外分块在思维上比其他正解是要好想的在想不到正解的时候可以考虑分块暴力草三、题目1. Give Away#includebits/stdc.h using namespace std; /* /\_/\ * ( ._.) * / \ */ /* *想好再写 *注意审题 注意特判 *不要红温 不要急躁 耐心一点 *WA了不要立马觉得是思路不对 先耐心找反例 */ #define endl \n #define dbg(x) cout#xendl;coutxendl; #define vdbg(a) cout#aendl;for(auto x:a)coutx ;coutendl; #define YES coutYESendl;return ; #define Yes coutYesendl;return ; #define NO coutNOendl;return ; #define No coutNoendl;return ; typedef long long ll; typedef long double ld; typedef pairint,int pii; typedef pairll,ll pll; const int INF1e9; const ll INFLL1e18; const int dx[]{-1,1,0,0}; const int dy[]{0,0,-1,1}; const int ddx[]{-2,-1,1,2,2,1,-1,-2}; const int ddy[]{1,2,2,1,-1,-2,-2,-1}; void solve() { int n; cinn; vectorinta(n1); for(int i1;in;i) { cina[i]; } int blensqrt(n); int bnum(nblen-1)/blen; vectorintbi(n1); for(int i1;in;i) { bi[i](i-1)/blen1; } vectorintbl(blen1); vectorintbr(blen1); for(int i1;ibnum;i) { bl[i](i-1)*blen1; br[i]min(i*blen,n); } vectorintsorted(n1); for(int i1;in;i) { sorted[i]a[i]; } for(int i1;ibnum;i) { sort(sorted.begin()bl[i],sorted.begin()br[i]1); } auto bs[](int i,int v)-int { int lbl[i]; int rbr[i]; int m; int ansr1; while(lr) { mlr1; if(sorted[m]v) { ansm; rm-1; } else { lm1; } } return br[i]-ans1; }; auto query[](int l,int r,int v)-int { if(bi[l]bi[r]) { int ans0; for(int il;ir;i) { if(a[i]v) { ans; } } return ans; } int ans0; for(int il;ibr[bi[l]];i) { if(a[i]v) { ans; } } for(int ibi[l]1;ibi[r]-1;i) { ansbs(i,v); } for(int ibl[bi[r]];ir;i) { if(a[i]v) { ans; } } return ans; }; auto update[](int p,int v)-void { int lbl[bi[p]]; int rbr[bi[p]]; a[p]v; for(int il;ir;i) { sorted[i]a[i]; } sort(sorted.begin()l,sorted.begin()r1); }; int m; cinm; int op,i,x,l,r,v; while(m--) { cinop; if(op0) { cinlrv; coutquery(l,r,v)endl; } else { cinix; update(i,x); } } } void init() { } signed main() { ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int t1; //cint; init(); while(t--) { solve(); } return 0; }对于单点修改操作直接去每个位置修改即可。对于区间查询大于等于 v 的个数操作考虑每次单点修改后对这个块排序那么单点修改的复杂度就是的了。那么在保证每个块内有序后对于一个区间中间被完全包含的块就可以暴力枚举每次通过二分查找大于等于 v 的个数。而对于这个区间左右剩下的散点因为每个块的大小也是的所以可以直接去暴力计算大于等于 v 的个数。那么两个操作的复杂度就都是的了。实现时考虑定义 blen 为每个块的长度bnum 为块数注意要向上取整。再定义为 i 位置所在的块的编号和分别为第 i 个块的左右边界。在查询时注意若区间在同一个块里那么就直接暴力遍历即可。2. 教主的魔法#include bits/stdc.h using namespace std; /* /\_/\ * ( ._.) * / \ */ /* *想好再写 *注意审题 注意特判 *不要红温 不要急躁 耐心一点 *WA了不要立马觉得是思路不对 先耐心找反例 */ #define endl \n #define dbg(x) cout#xendl;coutxendl; #define vdbg(a) cout#aendl;for(auto x:a)coutx ;coutendl; #define YES coutYESendl;return ; #define Yes coutYesendl;return ; #define NO coutNOendl;return ; #define No coutNoendl;return ; typedef long long ll; typedef long double ld; typedef pairint,int pii; typedef pairll,ll pll; const int INF1e9; const ll INFLL1e18; const int dx[]{-1,1,0,0}; const int dy[]{0,0,-1,1}; const int ddx[]{-2,-1,1,2,2,1,-1,-2}; const int ddy[]{1,2,2,1,-1,-2,-2,-1}; void solve() { int n,q; cinnq; vectorlla(n1); for(int i1;in;i) { cina[i]; } int blensqrt(n); int bnum(nblen-1)/blen; vectorintbi(n1); for(int i1;in;i) { bi[i](i-1)/blen1; } vectorintbl(bnum1); vectorintbr(bnum1); for(int i1;ibnum;i) { bl[i](i-1)*blen1; br[i]min(n,i*blen); } vectorintsorted(n1); for(int i1;in;i) { sorted[i]a[i]; } for(int i1;ibnum;i) { sort(sorted.begin()bl[i],sorted.begin()br[i]1); } vectorlllazy(bnum1); auto bs[](int i,int v)-int { int lbl[i]; int rbr[i]; int m; int ansr1; while(lr) { mlr1; if(sorted[m]v) { ansm; rm-1; } else { lm1; } } return br[i]-ans1; }; auto add[](int l,int r,int v)-void { if(bi[l]bi[r]) { for(int il;ir;i) { a[i]v; } for(int ibl[bi[l]];ibr[bi[l]];i) { sorted[i]a[i]; } sort(sorted.begin()bl[bi[l]],sorted.begin()br[bi[l]]1); return ; } for(int il;ibr[bi[l]];i) { a[i]v; } for(int ibl[bi[l]];ibr[bi[l]];i) { sorted[i]a[i]; } sort(sorted.begin()bl[bi[l]],sorted.begin()br[bi[l]]1); for(int ibi[l]1;ibi[r]-1;i) { lazy[i]v; } for(int ibl[bi[r]];ir;i) { a[i]v; } for(int ibl[bi[r]];ibr[bi[r]];i) { sorted[i]a[i]; } sort(sorted.begin()bl[bi[r]],sorted.begin()br[bi[r]]1); }; auto query[](int l,int r,int v)-int { if(bi[l]bi[r]) { int ans0; for(int il;ir;i) { if(a[i]v-lazy[bi[l]]) { ans; } } return ans; } int ans0; for(int il;ibr[bi[l]];i) { if(a[i]v-lazy[bi[l]]) { ans; } } for(int ibi[l]1;ibi[r]-1;i) { ansbs(i,v-lazy[i]); } for(int ibl[bi[r]];ir;i) { if(a[i]v-lazy[bi[r]]) { ans; } } return ans; }; char op;int l,r,v; while(q--) { cinoplrv; if(opM) { add(l,r,v); } else { coutquery(l,r,v)endl; } } } void init() { } signed main() { ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int t1; //cint; init(); while(t--) { solve(); } return 0; }和上个题很类似就是单点修改改成区间增加了。那么和线段树的处理方法类似设置一个懒信息数组 lazy每次区间增加时对于左右的散点暴力中间的块直接增加懒信息。在区间查询的时候块内查询大于等于 v-lazy 的个数即可。3. 蒲公英#include bits/stdc.h using namespace std; /* /\_/\ * ( ._.) * / \ */ /* *想好再写 *注意审题 注意特判 *不要红温 不要急躁 耐心一点 *WA了不要立马觉得是思路不对 先耐心找反例 */ #define endl \n #define dbg(x) cout#x xendl; #define vdbg(a) cout#aendl;for(auto x:a)coutx ;coutendl; #define YES coutYESendl;return ; #define Yes coutYesendl;return ; #define NO coutNOendl;return ; #define No coutNoendl;return ; #define popcount __builtin_popcount using lllong long; using i128__int128; using ldlong double; using piipairint,int; using pllpairll,ll; const int INF1e9; const ll INFLL1e18; const int dx[]{-1,1,0,0}; const int dy[]{0,0,-1,1}; const int ddx[]{-2,-1,1,2,2,1,-1,-2}; const int ddy[]{1,2,2,1,-1,-2,-2,-1}; void solve() { int n,q; cinnq; vectorinta(n1); for(int i1;in;i) { cina[i]; } vectorintsorteda; sort(sorted.begin()1,sorted.end()); int len1; for(int i2;in;i) { if(sorted[len]!sorted[i]) { sorted[len]sorted[i]; } } auto rnk[](int v)-int { int l1; int rlen; int m; int ans; while(lr) { mlr1; if(sorted[m]v) { ansm; rm-1; } else { lm1; } } return ans; }; for(int i1;in;i) { a[i]rnk(a[i]); } int blensqrt(n); int bnum(nblen-1)/blen; vectorintbi(n1); for(int i1;in;i) { bi[i](i-1)/blen1; } vectorintbl(bnum1); vectorintbr(bnum1); for(int i1;ibnum;i) { bl[i](i-1)*blen1; br[i]min(n,i*blen); } vectorvectorintfreq(bnum1,vectorint(len1)); for(int i1;ibnum;i) { for(int j1;jlen;j) { freq[i][j]freq[i-1][j]; } for(int jbl[i];jbr[i];j) { freq[i][a[j]]; } } vectorvectorintmode(bnum1,vectorint(bnum1)); for(int i1;ibnum;i) { for(int ji;jbnum;j) { int mostmode[i][j-1]; int mostCntfreq[j][most]-freq[i-1][most]; for(int kbl[j];kbr[j];k) { int cura[k]; int curCntfreq[j][cur]-freq[i-1][cur]; if(curCntmostCnt||(curCntmostCntcurmost)) { mostcur; mostCntcurCnt; } } mode[i][j]most; } } vectorintcnts(len1); int l,r; int pre0; while(q--) { cinlr; l(lpre-1)%n1; r(rpre-1)%n1; if(lr) { swap(l,r); } if(bi[l]bi[r]) { for(int il;ir;i) { cnts[a[i]]; } int most0; for(int il;ir;i) { if(cnts[a[i]]cnts[most]||(cnts[a[i]]cnts[most]a[i]most)) { mosta[i]; } } presorted[most]; coutpreendl; for(int il;ir;i) { cnts[a[i]]0; } continue; } for(int il;ibr[bi[l]];i) { cnts[a[i]]; } for(int ibl[bi[r]];ir;i) { cnts[a[i]]; } int mostmode[bi[l]1][bi[r]-1]; int mostCntfreq[bi[r]-1][most]-freq[bi[l]][most]; for(int il;ibr[bi[l]];i) { int cura[i]; int curCntcnts[cur]freq[bi[r]-1][cur]-freq[bi[l]][cur]; if(curCntmostCnt||(curCntmostCntcurmost)) { mostcur; mostCntcurCnt; } } for(int ibl[bi[r]];ir;i) { int cura[i]; int curCntcnts[cur]freq[bi[r]-1][cur]-freq[bi[l]][cur]; if(curCntmostCnt||(curCntmostCntcurmost)) { mostcur; mostCntcurCnt; } } presorted[most]; coutpreendl; for(int il;ibr[bi[l]];i) { cnts[a[i]]0; } for(int ibl[bi[r]];ir;i) { cnts[a[i]]0; } } } /* 6 3 1 2 3 2 1 2 1 5 3 6 1 5 1 2 1 */ void init() { } signed main() { ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int t1; //cint; init(); while(t--) { solve(); } return 0; }对于众数这个问题首先可以考虑维护 freq[i][j] 表示前 i 块里 j 这个数出现的次数即一个前缀词频。之后再维护一个 mode[i][j] 表示第 i 块到第 j 块之间的最小众数。那么对于查询中间的整块就可以使用这两个数组查出众数和词频。然后对于两边的散块就可以暴力统计词频判断是否大于中间这个众数即可。构建时freq 数组就是正常统计前缀词频即可。对于 mode 数组就是每次先找出 i 到 j-1 块的众数和词频然后在遍历当前块的时候对于块内的每个数判断其区间词频是否可以更新众数即可。总结分块暴力的神END
延伸阅读

更多相关文章

2026/10/29 10:34:55

Windows服务内存泄漏排查实战:系统池异常增长与句柄泄漏修复

1. 问题现象与服务现状先交代一下背景。我手里维护着一个叫GODService的Windows后台服务,这个服务本身不算复杂,核心工作就是长期驻留、定时采集系统运行数据、按需监听一些事件并写日志。部署环境大多数是Windows 11专业版工作站和少量Windows Server 2…

2026/10/29 10:29:54

GPU加速TTI正演与逆时偏移:CUDA实现与ADCIGS生成

简介:面向地震波场模拟与高性能计算方向的研究者与工程师,这份资源聚焦二维TTI各向异性介质下的有限差分正演与逆时偏移实现,并借助CUDA将计算密集的波场传播与成像过程迁移到GPU并行加速,适合已具备波动方程与有限差分基础、希望…

2026/10/29 11:40:19

原生JavaScript实现TodoList:前端基础项目实战

1. 项目概述:为什么选择TodoList作为前端练手项目 TodoList(待办事项列表)可以说是前端开发者最好的"Hello World"项目之一。它涵盖了前端开发最核心的几项能力:DOM操作、事件处理、数据管理和本地存储。我在带新人时发…

2026/10/29 11:40:19

JavaScript实战:从零构建TodoList应用

1. 项目概述:为什么选择TodoList作为JavaScript实战项目? TodoList(待办事项列表)可以说是前端开发者最好的"Hello World"项目之一。作为一个看似简单的应用,它完美涵盖了前端开发的核心技术栈:D…

2026/10/29 11:40:19

安全生产月知识竞赛全流程策划与执行指南

1. 项目概述 "安全生产月知识竞赛"是每年6月全国安全生产月期间广泛开展的一项重要活动。作为企业安全管理部门负责人,我连续5年主导策划并执行了这项活动,累计参与员工超过3000人次。这种竞赛形式不仅能有效检验员工安全知识掌握程度&#xf…

2026/10/29 11:40:19

C语言switch与goto在逆向工程中的深度解析

1. 项目概述 作为一名有着15年C语言开发经验的工程师,我经常看到初学者对switch和goto这两个流程控制语句存在诸多误解。今天我们就来深入探讨这两个看似简单却暗藏玄机的语法结构,特别是在逆向工程领域的特殊应用场景。 在逆向分析过程中,理…

2026/10/29 11:40:19

AI智能体开发:从伪智能到真自主的技术解析

1. 项目概述:揭开AI智能体的真实面纱 最近在开发者社区里有个现象特别有意思:随便打开一个技术论坛,几乎每三天就能看到一个新发布的"AI智能体"项目。这些项目往往打着"完全自主"、"类人思考"、"无需干预…

2026/10/29 11:35:10

算法竞赛必备:Acwing模板背诵与实战优化技巧

1. 为什么算法模板需要专门背诵? 第一次接触算法竞赛时,我也曾疑惑:为什么不能现场推导?实战中才明白,比赛时的紧张氛围和严格时间限制,让模板背诵成为必备技能。就像篮球运动员必须把标准投篮动作练成肌肉…

2026/10/28 14:42:12

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/28 15:28:56

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/28 3:32:29

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/29 0:03:42

学生选课系统DFD图绘制指南:边界划分与分层校验实战

简介:文档以顶层与第一层DFD图为核心,完整刻画学生选课系统的逻辑模型,面向软件工程课程设计、系统分析学习及毕设前期建模人群。顶层图界定系统边界,外部实体包括管理员、教师和学生三类角色;第一层图进一步细化用户登…

2026/10/29 0:03:42

JIRA敏捷落地:Scrum角色权限、Sprint与燃尽图避坑指南

简介:围绕“基于JIRA的敏捷开发项目管理”这一主题,面向产品负责人、Scrum Master及研发测试成员,梳理Scrum角色分工、迭代流程与JIRA工具配置的对应关系,适合初、中级团队导入敏捷管理时参考。压缩包共1个docx文件,大…

2026/10/29 0:03:42

SEMI S8人机工程验收实战:从条款拆解到现场实测的完整指南

简介:面向半导体设备设计、制造与运维工程师及安全合规人员,这份资料聚焦SEMI标准体系下的设备安全规范,重点收录SEMI S8-0308《半导体制造设备人体工程学工程安全指南》。内容围绕设备设计原则、错误与事故预防、人体工程学适配三大主线展开…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑