RMQ 问题——ST表

RMQ问题

不带修改的区间最值,重叠的区间不会对区间的最大值产生影响

可以用 ST表(稀疏表)
(不带修改的区间问题可以用,一经修改就不可以用了)

例题模板:区间最值


 int dp[8][35];//dp[i][j]表示左端点为i,长度为2^j这样的长度的区间,也就是<==>ans[i][i+2^j-1]


 int query(int l, int r )
 {
	int j = (int)log2(r - l + 1);
	 return max(dp[l][j], dp[r - (1 << j) + 1][j]);//区间最大值可以有重合覆盖上,右边长度还是j
 }



 int main()
 { 
	 int arr[8] = { 9,3,1,7,5,6,0,8 };
	 int n = 8;
//填充dp[i][j]
	 for (int i = 0; i < n; i++)
	 {
		 dp[i][0] = arr[i];//ans[i][i+2^0-1]=arr[i]
	 }

	 for (int j = 1; j <= log2(n); j++)//j=0已经处理了,先要枚举j,而不是先枚举i,j的最大长度是log2(n);
	 {
		 for (int i = 0; i + (1 << j) - 1 < n; i++)// i + (1 << j) - 1是区间的右端点,要小于n,不要越界,
		 {
			 dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);//把一个长的区间,把他砍一半,,一半一半的求
			 //j-1相当于区间长度取了一半,一半一半的求最值
			 // dp[i][j]的值为,最值(dp[i][j-1]相当于[i][i+2^(j-1)-1]的区间,与[i+2^(j-1)][i+2^(j-1)+2^(j-1)-1]的最值

		 }

	 }
	 int l, r;//左右断电
	 while (cin >> l >> r)
	 {
		 cout << query(l, r) << endl;//query就是询问函数询问l,r的区间最大值
	 }
	 return 0;
 }


例题:区间最大公约数


int gcd(int a,int b)
{
return b?gcd(b,a%b):0;
}

int dp[8][35];//dp[i][j]表示左端点为i,长度为2^j这样的长度的区间,也就是<==>ans[i][i+2^j-1]


int query(int l, int r)
{
	int j = (int)log2(r - l + 1);
	return gcd(dp[l][j], dp[r - (1 << j) + 1][j]);//区间最大值可以有重合覆盖上,右边长度还是j
}



int main()
{
	int arr[8] = { 9,3,15,12,5,6,0,8 };
	int n = 8;
	//填充dp[i][j]
	for (int i = 0; i < n; i++)
	{
		dp[i][0] = arr[i];//ans[i][i+2^0-1]=arr[i]
	}

	for (int j = 1; j <= log2(n); j++)//j=0已经处理了,先要枚举j,而不是先枚举i,j的最大长度是log2(n);
	{
		for (int i = 0; i + (1 << j) - 1 < n; i++)// i + (1 << j) - 1是区间的右端点,要小于n,不要越界,
		{
			dp[i][j] = gcd(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);//把一个长的区间,把他砍一半,,一半一半的求
			//j-1相当于区间长度取了一半,一半一半的求最值
			// dp[i][j]的值为,最值(dp[i][j-1]相当于[i][i+2^(j-1)-1]的区间,与[i+2^(j-1)][i+2^(j-1)+2^(j-1)-1]的最值

		}

	}
	int l, r;//左右断电
	while (cin >> l >> r)
	{
		cout << query(l, r) << endl;//query就是询问函数询问l,r的区间最大值
	}
	return 0;
}

区间最大间距

#include<iiostream>
using namespace std;
int dpmax[30][35];
int dpmin[30][35];
//区间最值差

int querymax(int l, int r)
{
	int j =(int) log2(r - l + 1);
	return max(dpmax[l][j], dpmax[r - (1 >> j) + 1][j] );
}


int querymin(int l, int r)
{
	int j = (int)log2(r - l + 1);
	return min(dpmin[l][j], dpmin[r - (1 << j) + 1][j]);//区间最大值可以有重合覆盖上,右边长度还是j
}

int main()
{
	int n,m;
	cin >> n>>m;
	int arr[25]; 
	//预处理
	for(int i = 0; i < n; i++)
	{
		cin >> arr[i];
	dpmax[i][0] = arr[i];
	dpmin[i][0] = arr[i];
	}
	for (int j = 1; j <= log2(n); j++)
	{
		for (int i = 0; i + (1 << j) - 1 < n; i++)
		{
			dpmax[i][j]=max(dpmax[i][j-1],dpmax[i+(1<<(j-1))][j-1]);
		    dpmin[i][j]=min(dpmin[i][j - 1], dpmin[i + (1 << (j - 1))][j-1]);

		}
		}
	while (m--)
	{
		int l, r;
		cin >> l >> r;
		cout << querymax(l,r)-querymin(l,r) << endl;
	}
		return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevin~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值