首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

离线算法 莫队算法进阶

编程知识
2024年08月18日 16:32

算是把之前的坑填一填吧。

这篇文章主要包含带修莫队,二维莫队等莫队算法的进阶应用,观看前请确保您已经熟练掌握了基本的莫队算法,不会的可以戳这里


带修莫队

众所周知,普通莫队是不支持修改的,因为我们为了得到更优的时间复杂度,需要将每次询问离线下来,打乱顺序。

不过我们也可以通过加上一维时间维强行让它支持修改,这里的时间维在题目意义上就是需要进行的修改次数。

主要思想

原本我们的转移只有四种,以当前为 \(\left[l,r\right]\) 为例,我们可以 \(\mathcal{O(1)}\) 扩展到:

  • \(\left[l-1,r\right]\)

  • \(\left[l+1,r\right]\)

  • \(\left[l,r-1\right]\)

  • \(\left[l,r+1\right]\)

若添加上一维时间,那么我们需要进行六种转移:

  • \(\left[l-1,r,time\right]\)

  • \(\left[l+1,r,time\right]\)

  • \(\left[l,r-1,time\right]\)

  • \(\left[l,r+1,time\right]\)

  • \(\left[l,r,time-1\right]\)

  • \(\left[l,r,time+1\right]\)

只是多了种转移方式,并不影响我们转移的复杂度。

离线询问排序的原则,以左端点所在块为第一关键字,右端点所在块为第二关键字,时间为第三关键字升序排列。

块长选定与时间复杂度

块长方面,通常取 \(\mathcal{n^{\frac{2}{3}}}\),最终复杂度为 \(\mathcal{O(n^{\frac{5}{3}})}\)

关于最优块长的分析:

image

摘自 OI-wiki。

实现

P1903 数颜色/ 维护队列 为例。

带修莫队板子题,思路同上,这里主要讲一下两个时间维的转移。

思路很简单,当查询到某次询问 \(q\) 时,若当前时间小于该次时间,则进行转移。若某次更改的点恰好在当前区间范围内,则进行更改操作,更改操作可以视为一次删除和一次添加,操作完成后为了方便之后再回退回去,我们交换删除的元素和更新的元素。

操作分离:

for(int i=1;i<=m;i++)
{
	char op;int x,y;
	cin>>op>>x>>y;
	if(op=='Q')
		q[++qcnt].id=qcnt,q[qcnt].time=rcnt,
		q[qcnt].x=x,q[qcnt].y=y;
	else
		r[++rcnt].p=x,r[rcnt].x=y;
}

转移部分:

void add(int x)
{
	if(!mp[x]) anss++;
	mp[x]++;
}
void del(int x)
{
	mp[x]--;
	if(!mp[x]) anss--;
}
int main()
{
	/*code*/
	int L=1,R=0,tim=0;
	for(int i=1;i<=qcnt;i++)
	{// 转移原则:先扩大后缩小
		while(L>q[i].x) add(a[--L]);
		while(R<q[i].y) add(a[++R]);
		while(L<q[i].x) del(a[L++]);
		while(R>q[i].y) del(a[R--]);
		while(tim<q[i].time)
		{
			tim++;
			if(L<=r[tim].p&&r[tim].p<=R)
				add(r[tim].x),del(a[r[tim].p]);
			swap(a[r[tim].p],r[rim].x);
		}
		while(tim>q[i].time)
		{
			if(L<=r[tim].p&&r[tim].p<=R)
				add(r[tim].x),del(a[r[tim].p]);
			swap(a[r[tim].p],r[tim].x);
			tim--;
		}
		ans[q[i].id]=anss;
	}
}

二维莫队

普通的莫队算法处理的是序列上的问题,如果我们将它扩展成一个平面,使得转移时可以有四个方向选择,就得到了二维莫队。

二维莫队的转移操作每次移动指针要操作一行或一列的数,具体实现方式与普通的一维莫队类似。

块长选定与时间复杂度

image
摘自 OI-wiki。

简而言之,设矩阵大小为 \(S\),询问次数为 \(q\),我们的块长通常选定为 \(\frac{\sqrt S}{q^{\frac{1}{4}}}\),这样算得的结果若小于 1,我们直接为其赋值为 1 即可。

最终,在求解部分的时间复杂度是 \(\mathcal{O(S·q^{\frac{3}{4}})}\),总时间复杂度为 \(\mathcal{O(S·q^{\frac{3}{4}}+q\log q)}\)

实现

例题:给定一个大小为 \(n\times m\) 的矩阵,接着给出 \(q\) 个询问,每次询问一个子矩阵的权值,权值定义为:若一元素在某矩阵出现了 \(k\) 次,那么它的贡献就为 \(k^2\)

结构体定义:

给定一个矩形的左上角坐标和右下角坐标,我们按左上角横坐标、左上角纵坐标、右下角横坐标、右下角纵坐标所属块的顺序升序排列。

struct Que
{
	int lx,ly,rx,ry,id;
	bool operator<(const Que &A)const
	{
		if(lx/B!=A.lx/B) return lx<A.lx;
		if(ly/B!=A.ly/B) return ly<A.ly;
		if(rx/B!=A.rx/B) return rx<A.rx;
		return ry<A.ry;
	}
}que[N];

转移部分:

对于行与列,我们用不同的函数

int a[N][N];// 原矩阵
int cnt[N];// 某数出现的
void mdfline(int id,int val,int u,int v)
{// 增加/删除某一行
	for(int i=u;i<=v;i++)
		anss-=1ll*cnt[a[id][i]]*cnt[a[id][i]],
		cnt[a[id][i]]+=val,
		anss+=1ll*cnt[a[id][i]]*cnt[a[id][i]];
}
void mdfcolumn(int id,int val,int u,int v)
{
	for(int i=u;i<=v;i++)
		anss-=1ll*cnt[a[i][id]]*cnt[a[i][id]],
		cnt[a[i][id]]+=val,
		anss+=1ll*cnt[a[i][id]]*cnt[a[i][id]];
}
int main()
{
	/*code*/
	B=pow(n*m,0.5)/pow(q,0.25);
	if(B<1) B=1;
	sort(que+1,que+1+q);
	int lx=1,ly=1,rx=0,ry=0;
	for(int i=1;i<=q;i++)
	{
		while(lx>que[i].lx) mdfline(--lx,1,ly,ry);
		while(rx<que[i].rx) mdfline(++rx,1,ly,ry);
		while(ly>que[i].ly) mdfcolumn(--ly,1,lx,rx);
		while(ry<que[i].ry) mdfcolumn(++ry,1,lx,rx);
		while(lx<que[i].lx) mdfline(lx++,-1,ly,ry);
		while(rx>que[i].rx) mdfline(rx--,-1,ly,ry);
		while(ly<que[i].ly) mdfcolumn(ly++,-1,lx,rx);
		while(ry>que[i].ry) mdfcolumn(ry--,-1,lx,rx);
		ans[que[i].id]=anss;
	}
	for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
	return 0;
}

这些多多少少的莫队算法的改良都是根据不同的题型进行针对化的优化而来的,应用较为局限。但是这些优化的思想仍然对我们做题有很大的帮助,我们仍然要努力弄懂每一种算法,万一哪天就用到了呢?

本文代码全部线上手打,有问题请指出,感谢支持。


完结撒花~

From:https://www.cnblogs.com/Ratio-Yinyue1007/p/18365270
本文地址: http://shuzixingkong.net/article/1205
0评论
提交 加载更多评论
其他文章 flink + iceberg 快速搭建指南
flink + iceberg 快速搭建 the environment includes: minio iceberg flink Centos 更换 tencent 的yum源 备份系统旧配置文件 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.rep
超越Perplexity的AI搜索引擎框架MindSearch
MindSearch 是InternLM团队的一个开源的 AI 搜索引擎框架,由中科大和上海人工智能实验室联合打造的,具有与 Perplexity.ai Pro 相同的性能。本文介绍MindSearch 的相关原理。
超越Perplexity的AI搜索引擎框架MindSearch 超越Perplexity的AI搜索引擎框架MindSearch 超越Perplexity的AI搜索引擎框架MindSearch
AvaloniaChat—从源码构建指南
AvaloniaChat介绍 一个使用大型语言模型进行翻译的简单应用。 我自己的主要使用场景 在看英文文献的过程中,比较喜欢对照着翻译看,因此希望一边是英文一边是中文,虽然某些软件已经自带了翻译功能,但还是喜欢大语言模型的翻译,但每次都要将英文复制粘贴过去还要自己手动添加prompt,还无法对照着看
AvaloniaChat—从源码构建指南 AvaloniaChat—从源码构建指南 AvaloniaChat—从源码构建指南
.NET 9发布的最后一个预览版Preview 7, 下个月发布RC
微软在2024年8月9日 发布了.NET 9 Preview 7[1],这是它在2024 年 11 月 12 日 RTM 之前进入发布候选阶段之前的最后预览版, 将在.NET Conf 2024 一起发布[3]。该预览版也于也与 Visual Studio 2022 17.12 预览版1一起发布,现
CORDIC算法解释及verilog HDL实现(圆坐标系)
本文阐述Cordic算法在圆坐标系下关于旋转和向量模式两种不同的实现路径,并通过了Matlab程序编写实现以及Verilog HDL在此算法的仿真验证。
CORDIC算法解释及verilog HDL实现(圆坐标系) CORDIC算法解释及verilog HDL实现(圆坐标系) CORDIC算法解释及verilog HDL实现(圆坐标系)
代码随想录Day17
654.最大二叉树 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点,其值为 nums 中的最大值。 递归地在最大值 左边 的 子数组前缀上 构建左子树。 递归地在最大值 右边 的 子数组后缀上 构建右子树。 返回 nums 构建的 最大
Java Web中的request,response,重定位与转发的详解
request与response响应 Web服务器接收到客户端的http请求,其会对每一次的http请求分别创建应该代表请求的request对象,和一个代表响应的response对象. request是获取客户端提交的数据,response是向客户端提供数据. request 一个request请求
Java Web中的request,response,重定位与转发的详解
Elsa V3学习之Hello Word
前面文章介绍了Elsa的基础节点内容,接下来我们来开始实践一下。 启动项目 启动源码目录src\bundles中的Elsa.ServerAndStudio.Web的项目。这个项目包含Elsa Server以及前端界面。可以让我们快速学习Elsa项目。 控制台Hello Word 打开Workflow
Elsa V3学习之Hello Word Elsa V3学习之Hello Word Elsa V3学习之Hello Word