首页 > 其他分享 >分块相关题目

分块相关题目

时间:2024-02-28 15:36:36浏览次数:24  
标签:题目 分块 List pos sqrt Next 插入 相关 散块

题单

UPD:题单里的题 \(n=m\)。

数列分块入门一

看到区间修改 \(+\) 单点查询,考虑差分。

考虑分块维护差分数组。对于修改操作,就对 \(l\) 位置 \(+k\),\(r+1\) 位置 \(-k\);对于查询操作,查询 \([1,x]\) 的和即可。

时间复杂度 \(O(m\sqrt{n})\),可以通过。

代码

数列分块入门二

如果直接查找,那么复杂度是线性的,因此我们考虑让原序列有序。

一个简单的思路;一开始将每个块排序,然后修改整块打标记,散块暴力修改。对于查询操作,散块暴力统计,整块直接二分。

可惜这样是错的。

原因很简单,排序会破坏原数组的顺序,这样散块的结果就不对了。

考虑设辅助数组 \(b\),初始为 \(a\) 并将其排序。修改操作整块直接打标记,散块对 \(a\) 修改再拍到 \(b\) 上面重新排序。对于查询操作,整块二分,散块暴力查找 \(a\)。

注意整块二分的时候要先减去该块的标记再对 \(b\) 进行二分。

时间复杂度 \(O(m\sqrt{n}\log{\sqrt{n}})\),可以通过。

代码

更优的做法:对每个块排序,记录每个元素原来的位置。然后对零散块的修改就可以直接归并,砍掉一只 \(\log\)。

然后根号平衡随便算算可知块长取 \(\sqrt{\frac{n}{\log n}}\) 最优,实际测试取 \(135\sim 150\) 最优,运行时间 \(390\) ms 左右。

块长取常数!!!

代码

类似的题:P2801 教主的魔法。只需要改改数据范围,开 long long 即可。

数列分块入门三

仍然可以像上个题一样二分查找。

但是还有一种思路:对每个块维护一个 multiset。修改时,整块打标记,散块在 multiset 中删除原数 \(\to\) 原数 \(+c\to\) 插入新数;对于查询,整块二分查找前驱(lower_bound-1),散块暴力。

时间复杂度 \(O(m\sqrt{n}\log{\sqrt{n}})\),可以通过。

代码

数列分块入门四

考虑维护每个块的和。

对于修改操作,整块打标记,散块暴力修改;对于查询操作,整块加上 \(\text{区间和}+\text{区间长度}\times\text{标记}\),散块暴力,但是也要加上标记。

时间复杂度 \(O(m\sqrt{n})\),可以通过此题。

代码

数列分块入门五

乍一看好像不好维护,考虑挖掘一下开方的性质。

实际上,对于 \(2^{31}-1\) 来说,只需要经过五次开方就会 变成 \(1\),之后便不会变化。因此可以考虑维护每个块被开方的次数 \(cnt_x\),修改时整块如果 \(cnt\ge5\) 就直接退出,否则暴力修改,散块直接暴力。这样时间复杂度是对的,因为每个数至多被修改 \(5\) 次。

对于查询操作,只需要在修改的同时顺便维护下区间和即可。

时间复杂度 \(O(m\sqrt{n})\),可以通过此题。

代码

数列分块入门六

块状链表板子。

我们考虑维护这么一个数据结构:内部是一个不定长的块,并设置一个最大长度 \(LEN\);块与块直接用链表连起来。对于这道题来说就是一个 list<vector<int> >

接下来考虑实现如下操作:

零、定义

list<vector<int> > List;
typedef list<vector<int> >::iterator IT;

一、初始化

由于这个题一开始有 \(n\) 个元素,因此直接读入到一个 vector 中并插入块状链表即可。

vector<int>a;
for(int i=1;i<=n;++i){
	int x=read();
	a.emplace_back(x);
}
List.insert(List.end(),a);

关于 std::listinsert 函数请自行 BDFS。

二、查找某个位置所在块

在对某个位置的元素进行操作时,往往需要求出它处于哪个块中,并更新它为在其所在块中的位置,便于在块中进行访问。

做法:枚举每一个块计算 size 即可。

inline IT find(int &pos){
	// 返回 pos 所在块,pos 变为在其所在块的位置 
	for(IT i=List.begin();;++i){
		if(i==List.end()||pos<=(int)i->size())return i;
		pos-=i->size();
	}
}

三、查询某一位置的值

我们假定下标从 \(1\) 开始。

我们首先调用 find 函数求出当前位置所在的块 it 及其在所在块的位置 pos。由于容器的下标是从 \(0\) 开始的,因此调用 it->at(pos-1)

inline int get(int pos){
	// 查询第 pos 个元素的值
	IT it=find(pos);
	return it->at(pos-1);
}

四、后继

查找某个块的后继。

指针 \(++\) 即可。

inline IT Next(IT x){return ++x;}

五、合并

假设要合并 \(x\) 块和 \(Next(x)\) 块。

我们只需要将 \(Next(x)\) 中的所有元素复制到 \(x\) 的最后面,然后删除 \(Next(x)\) 即可。

inline void Merge(IT x){
	// merge x and x+1
	x->insert(x->end(),Next(x)->begin(),Next(x)->end());
	List.erase(Next(x));
}

六、分裂

这次我们要将块 \(x\) 在 \(pos\) 的位置后面分裂,\(pos\) 成为前一段的最后一个元素(\(pos\) 从 \(1\) 开始)。

首先特判一下 \(pos\) 在块尾的情况,此时不需要分裂。

然后把 \(pos\) 那一段插入到 \(Next(x)\) 前面,然后在 \(x\) 中删除这一段即可。

inline void Split(IT x,int pos){
	// 将第 x 块从第 pos 个元素后面分开 
	if(pos==(int)x->size())return;
	List.insert(Next(x),vector<int>(x->begin()+pos,x->end()));
	x->erase(x->begin()+pos,x->end());
}

七、重构

这个是重点。

由于频繁插入后可能会使一个块的大小过大,从而浪费时间,因此需要将这些块重新分裂、合并。

具体操作是,扫描每一个块,如果其大小超过 \(2\times LEN\),那么就一直将其末尾分裂为长度为 \(LEN\) 的块,直至其满足条件为止;如果当前块不为最尾部的块,且 \(x\) 块和 \(Next(x)\) 块的大小之和都小于 \(LEN\),就合并这两块;最后,将末尾被删光的空块删除。

一次重构的最坏复杂度是 \(O(n)\) 的,但是其平均操作次数远小于 \(O(n)\),可以近似认为是 \(O(\sqrt{n})\) 级别。

inline void rebuild(){
	// 重构 
	for(IT i=List.begin();i!=List.end();++i){
		while(i->size()>=(LEN<<1)) Split(i,i->size()-LEN);
		while(Next(i)!=List.end()&&i->size()+Next(i)->size()<=LEN) Merge(i);
		while(Next(i)!=List.end()&&!Next(i)->size()) List.erase(Next(i));
	}
}

八、插入元素

插入分为在前面插入和在后面插入两种。本题为在前面插入,但实际上在第 \(x\) 个元素前面插入等同于在第 \(x-1\) 个元素后面插入,所以只讨论在后面插入的情况。

首先将第 \(x\) 个元素所在块在 \(x\) 所在的位置分裂,然后在 \(Next(x)\) 面前插入待插入元素即可(本题是一个长为 \(1\),元素为插入元素的 vector)。

inline void insert(int pos,int x){
	// 在 pos 前面插入 x 
	pos--;
	IT now=find(pos);
	if(List.size())Split(now,pos);
	List.insert(Next(now),vector<int>(1,x));
	rebuild();
}

这个是在前面插入的,在后面插入把 pos-- 去掉即可。

然后这个题需要维护的操作就没了,注意初始化的时候也要重构一次。

代码outputdebug)。

P4008 [NOI2003] 文本编辑器

标签:题目,分块,List,pos,sqrt,Next,插入,相关,散块
From: https://www.cnblogs.com/syzqwq/p/18040572

相关文章

  • GitHub上整理的一些工具(Web 前端相关)
    Web服务器性能/压力测试工具/负载均衡器http_load:程序非常小,解压后也不到100Kwebbench:是Linux下的一个网站压力测试工具,最多可以模拟3万个并发连接去测试网站的负载能力ab:ab是apache自带的一款功能强大的测试工具Siege:一款开源的压力测试工具,可以根据配置对一个WEB......
  • 【题目】LuoguP1065
    准备机试,做两道题复健(这事我是不是干了好多次了)。https://www.luogu.com.cn/problem/P1065题意是有n个工件,每个工件有m道工序,每个工件的每道工序有其用时,同时对应一台机器,机器总共也有m台。每台机器同时只能处理一个工序。现给出工件的工序顺序,问尽可能靠前安排,所用的时间。 ......
  • USB协议相关
    1.包(Packet)       包(Packet)是USB系统中信息传输的基本单元,所有数据都是经过打包后在总线上传输的。数据在USB总线上的传输以包为单位,包只能在帧内传输。2.帧(Frame)     高速USB总线的帧周期为125us,全速以及低速USB总线的帧周期为1ms。帧的起始由一......
  • 分块一览
    前言如题。值域分块顾名思义,就是在桶上分块。它的用处是把区间修改和区间询问中某一种操作变成\(O(1)\),另一种变成\(O(\sqrtn)\)。所以经常用来辅助维护两种操作数量严重不对等的数据结构。典型代表有莫队和根号分治。这里看一个莫队的例子。如我们要维护一个二维数点......
  • 分块和莫队
    1分块1.1概念简述分块被称为“优雅的暴力”。对于一个区间$[1,n]$,我们将其分成若干个块。在处理整块时直接维护整块信息,达到降低时间复杂度的目的。对于常规分块,设块长为$m$,则一般情况下$m$取$\sqrt{n}$时复杂度最优。下面举几例来说明分块如何降低时间复杂度。1.2......
  • 大数据相关术语-cdc
    CDC数据领域中的CDC是变更数据捕获(ChangeDataCapture)的缩写。它是一种用于捕获数据库中数据变更的技术。CDC可以用于各种目的,例如:数据同步:将数据从一个数据库同步到另一个数据库。数据仓库:将数据从数据库加载到数据仓库。实时分析:捕获数据库中的实时变更并进行分析。......
  • .NET GUI 相关页面跳转方案
    1.NavigationView是UWP,及现在winui流行的主窗口导航方式。创建一个NavigationView,在里面放置Frame作为右侧主要的展示窗口。在CodeBehind中实现NavView的ItemInvoked事件。根据参数InvokedItem(每一个Item的Content名称),或者每一Item的Tag来确定跳转。(还需处理重复跳转......
  • 【学习笔记】分块学习笔记
    学习笔记索引分块经常听别人提起,我也学一下。正片分块就是将一个数列分成很多块,然后每块单独操作,最后的结果放到原数列里。分块的题目类型经常是区间中修改和查询。这里,一个长度为\(n\)的数列最多分成\(\sqrtn\)个块。先来看例题吧。例题P2357守墓人题目背景在一......
  • 决策单调性相关
    1.四边形不等式1.1小标题该怎么起阿考虑一个形式如下的DP:\[f_{i}=\min_{j=1}^{i}val(j,i)\]其中\(i\)满足\(1\leqi\leqn\)。设\(f_{i}\)的最优决策点为\(oper_{i}\)。1.2一些概念决策单调性:指满足\(\symbfit{oper_1\leqoper_2\leq\dots\leqoper_......
  • 2.微服务及相关框架
    1.微服务框架,微服务好处微服务(MicroServices),我们可以从字面上去理解,即“微小的服务”:(1) 所谓“服务”,其实指的是项目中的功能模块,它可以帮助用户解决某一个或一组问题,在开发过程中表现为IDE中的一个工程或Moudle。(2) “微小”则强调的是单个服务的大小,主要体现为以......