首页 > 其他分享 >RMQ问题的各种解法

RMQ问题的各种解法

时间:2024-04-05 22:33:23浏览次数:28  
标签:各种 log int void UP mid RMQ lz 解法

RMQ 问题:

给定一个序列,并有一些询问,每次询问一个区间的最大值或最小值。

下面以区间最大值为例进行讲解,设序列长度为 \(N\),有 \(M\) 次查询。

1 单调队列

前提条件

每个查询的区间互相不包含、离线、不能进行修改、不能在序列中增加元素。

思路

将所有查询按左端点排序(如果不保证左端点递增,则不用),由于互相不包含,此时右端点也递增。剩下就是单调队列的模板。

时间复杂度为 \(\mathcal{O}(N+Q \log Q)\)。如果保证左端点递增,则 \(\mathcal{O}(N+Q)\)。

例题 洛谷-P1440

代码

#include<cstdio>
#define UP(i,a,b) for(i=a;i<=(b);++i)
#define DN(i,a,b) for(i=a;i>=(b);--i)


const int N=2e6+5;
int a[N],n,m;
template<typename T>
class queue{
	private:
		T a[N],*h,*t;
	public:
		queue():h(a),t(a){}
		void push(T k){
			*t=k;++t;
		}
		void pop_back(){
			--t;
		}
		void pop_front(){
			++h;
		}
		T back(){
			return *(t-1);
		}
		T front(){
			return *h;
		}
		bool size(){
			return h!=t;
		}
};
queue<int> q;

int main(){
	int i;
	scanf("%d%d",&n,&m);
	UP(i,1,n){
		scanf("%d",a+i);
	}
	printf("0\n");
	UP(i,1,n-1){
		while(q.size()&&a[q.back()]>a[i]){
			q.pop_back();
		}
		while(q.size()&&q.front()<=i-m){
			q.pop_front();
		}
		q.push(i);
		printf("%d\n",a[q.front()]);
	}
	return 0;
}

2 ST 表

前提条件

不能进行修改、不能在序列中增加元素。

思路

预处理出序列的 ST 表,然后进行查询。

时间复杂度为 \(\mathcal(O)(N \log N+Q)\)。

例题 洛谷-P3865

代码

#include<cstdio>
#include<algorithm>
#define UP(i,a,b) for(i=a;i<=(b);++i)
#define DN(i,a,b) for(i=a;i>=(b);--i)

using std::max;

const int N=1e6+5;
int dp[N][25],log_2[N];
int query(int l,int r){
	int k=log_2[r-l+1];
	return max(dp[l][k],dp[r-(1<<k)+1][k]);
}
int main(){
	int n,m,i,j,l,r;
	scanf("%d%d",&n,&m);
	UP(i,1,n){
		scanf("%d",&dp[i][0]);
		if(i>1){
			log_2[i]=log_2[i>>1]+1;
		}
	}
	UP(j,1,log_2[n]){
		UP(i,1,n-(1<<j)+1){
			dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
		}
	}
	while(m--){
		scanf("%d%d",&l,&r);
		printf("%d\n",query(l,r));
	}
	return 0;
}

3 线段树

前提条件

不能在序列中增加元素。

思路

线段树向上维护区间最值即可,其他与线段树的模板相同。

时间复杂度为 \(\mathcal{O}((N+Q) \log N)\)。

例题

给定长度为 \(N\) 区间和 \(M\) 次查询,查询的格式为:
\(1,l,r,k\):将 \([l,r]\) 区间的所有数都加 \(k\)。
\(2,l,r\):求区间 \([l,r]\) 的最大值。

代码

#include<cstdio>
#include<algorithm>
#define UP(i,a,b) for(i=a;i<=(b);++i)
#define DN(i,a,b) for(i=a;i>=(b);--i)

using std::max;

const int N=1e5+5;
int a[N],n,m;
struct node{
	int t,lz;
}t[N<<2];

int ls(int p){
	return p<<1;
}
int rs(int p){
	return p<<1|1;
}
void push_up(int p){
	t[p].t=max(t[ls(p)].t,t[rs(p)].t);
}
void build(int p=1,int l=1,int r=n){
	int mid=(l+r)>>1;
	if(l==r){
		t[p].t=a[l];t[p].lz=0;
		return;
	}
	build(ls(p),l,mid);
	build(rs(p),mid+1,r);
	push_up(p);
}
void tag(int k,int p,int l,int r){
	t[p].t+=(r-l+1)*k;
	t[p].lz+=k;
}
void push_down(int p,int l,int r){
	int mid=(l+r)>>1;
	if(t[p].lz){
		tag(t[p].lz,ls(p),l,mid);
		tag(t[p].lz,rs(p),mid+1,r);
		t[p].lz=0;
	}
}
void update(int x,int y,int k,int p=1,int l=1,int r=n){
	int mid=(l+r)>>1;
	if(x<=l&&r<=y){
		tag(k,p,l,r);
		return;
	}
	push_down(p,l,r);
	if(x<=mid){
		update(x,y,k,ls(p),l,mid);
	}
	if(mid<y){
		update(x,y,k,rs(p),mid+1,r);
	}
	push_up(p);
}
int query(int x,int y,int p=1,int l=1,int r=n){
	int mid=(l+r)>>1,res=0;
	if(x<=l&&r<=y){
		return t[p].t;
	}
	push_down(p,l,r);
	if(x<=mid){
		res=max(res,query(x,y,ls(p),l,mid));
	}
	if(mid<y){
		res=max(res,query(x,y,rs(p),mid+1,r));
	}
	return res;
}
int main(){
	int i,o,l,r,k;
	scanf("%d%d",&n,&m);
	UP(i,1,n){
		scanf("%d",a+i);
	}
	build();
	UP(i,1,m){
		scanf("%d%d%d",&o,&l,&r);
		if(o==1){
			scanf("%d",&k);
			update(l,r,k);
		}else{
			printf("%d\n",query(l,r));
		}
	}
	return 0;
}

4 平衡树

前提条件

无前提条件。

思路

用平衡树实现线段树。因为平衡树可以插入结点,所以还可以在序列中插入元素。

时间复杂度为 \(\mathcal{O}((N+Q) \log N)\)。

这里用 Splay 树实现。

例题

给定长度为 \(N\) 区间和 \(M\) 次查询,查询的格式为:
\(1,l,r,k\):将 \([l,r]\) 区间的所有数都加 \(k\)。
\(2,x,y\):将 \([x,len]\) 中的所有元素都后移一位,并在第 \(x\) 位插入元素 \(y\)。
\(3,x\):删除第 \(x\) 个数。
\(4,l,r\):求区间 \([l,r]\) 的最大值。

代码

标签:各种,log,int,void,UP,mid,RMQ,lz,解法
From: https://www.cnblogs.com/lrxmg139/p/18116324

相关文章

  • 2024.4.5 RMQ补题
    P2048[NOI2010]超级钢琴前缀和处理连续子段的和弦,然后rmq求最大值运用堆存储最优答案每次取出堆头统计一次后,除掉统计点再分成两段加入即可,共k次#include<bits/stdc++.h>#definemaxn500010#defineINF0x3f3f3f3f#defineintlonglongusingnamespacestd;int......
  • 数论的各种板子2.0 (约数和 和 约数个数和)
    ​#include<bits/stdc++.h>//求约数和#include<map>usingnamespacestd;constintmod=1e9+10;typedeflonglongll;intmain(){ intn; cin>>n; unordered_map<int,int>primes; while(n--){ intx; cin>>x; for(inti=2......
  • Python 使用matplotlib创建各种静态、动态、交互式和3D图表的功能
    在Python中,你可以使用各种库来创建和显示图表。其中,最常用的库之一是matplotlib,它提供了创建各种静态、动态、交互式和3D图表的功能。另一个流行的库是seaborn,它基于matplotlib,并提供了更高级别的界面,用于绘制有吸引力的统计图形。以下是一个使用matplotlib创建并显示简单折线......
  • golang中各种状态下channel(管道)的读、写、close操作
    一、简介golang中各种状态下channel(管道)的读、写、close操作二、结论channel状态读写closeclose零值panicpanicnil永久阻塞(deadlock)永久阻塞(deadlock)panicbuffer满正常永久阻塞(deadlock)正常buffer空永久阻塞(deadlock)正常正常三、......
  • PowerShell 中,可以使用各种命令来收集系统信息。以下是一些常用的 PowerShell 信息收
    PowerShell中,可以使用各种命令来收集系统信息。以下是一些常用的PowerShell信息收集命令:获取计算机信息:Get-ComputerInfo:获取计算机的详细信息,包括操作系统版本、处理器、内存等。Get-WmiObject-ClassWin32_ComputerSystem:获取计算机系统信息,如制造商、型号、主机名等......
  • 数论的各种板子1.0
    仅为了记录所学的知识.boolis_prime(intx){//求是否为素数 if(x<2)returnfalse; for(inti=2;i<=x/i;++i){ if(x%i==0)returnfalse; } returntrue;}voiddivide(intx){//分解质因子 for(inti=2;i<=x/i;++i){ ints=0; while(x%i==0)x/=i,s++; cou......
  • java,postgresql,python中各种数据类型的占位长度,取值范围
    Java数据类型Java中的数据类型分为两类:基本数据类型和引用数据类型。基本数据类型数据类型占位长度取值范围byte1字节-128127short2字节-3276832767int4字节-21474836482147483647long8字节-92233720368547758089223372036854775807float4字节1.4E-453.4028235E38double8字节4.......
  • Leetcode--第1题(暴力解法C语言版)
    题目:给定一个整数数组nums和一个整数目标值target,请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。/***Note:Thereturnedarraymustbemalloced,assum......
  • 微分方程数值解法_常微分方程篇
    一阶常微分方程初值问题问题的适定性(well-posedness):(數學系的角度)•存在性:问题有解•唯一性:解是唯一的•稳定性:这个唯一解连续地依赖于问题中所给的数据(即初值、边值等)初值问题的求解Euler法區別(極限)入門要點:極限、中值定理==......
  • shiro的rememberMe各种漏洞一刀切解决
    rememberMe的低版本AES固定密码导致的漏洞,高版本仍然有被爆破,穷举的风险等。这种东西总是在安全检测的时候被拿出来说事儿,然而项目中并未开启rememberMe,也就是说压根不需要这个功能。那此时采用重写代码来直接杜绝这东西即可,任凭它怎么检测,已经没有这个口子了。跟踪源码会发现,......