sparse table

# sparse table 求解RMQ问题
来道水题

文章目录

在每天挤奶的时候,农民约翰的N头牛(1≤n≤50000)总是排成一列
有一天,约翰决定与他的牛们一起玩一个极限飞盘游戏。为了简单起见他将从奶牛队列里面选一定范围内的奶牛来玩这个游戏。然而所有的牛对这个游戏都很感兴趣。农民约翰列出了Q份名单(1≤Q≤200000)和每个奶牛的高度(1≤高度≤1000000)。对于每一份名单,他想你帮助他确定在每份名单中高度最高的奶牛与高度最低的奶牛的高度差是多少。

Input
第一行为N(1≤N≤50000)和Q(1≤Q≤200000);从第2行到第N+1行,每行一个数字,表示第i头牛的高度(1≤height≤1000000);从第N+2行到第N+Q+1行,每行两个整数A和B(1≤A≤B≤N),表示从第A头牛到第B头牛的范围。

Output
从第一行到第Q行,每行一个整数,表示从第A头牛到第B头牛之间,最高牛与最矮牛的高度差。

预处理:

采用DP的思想,f[i][j]表示[i , i+2∧j-1区间中的最小值(也就是从第i个数起连续2j个数中的最小值)。 数列3,2,4,5,6,8,1,2,9,7。f[1][0]=3,f[1][1]=2,f[1][2]=2,f[1][3]=1,f[2][0]=2,f[2][1]=2,f[2][2]=2…… f[i][j]可以由f[i][j-1]和f[i+2j-1][j-1]导出: f[i][j-1]表示区间[i , i+2j-1-1]的最小值 f[i+2j-1][j-1]表示区间[i+2^j-1^ , i+2[^j-1+2j-1-1]=[i+2j-1, i+2j-1] 的最小值 , 所以有DP方程: **f[i][j]=min(f[i][j-1],f[i+2[^j-1^][j-1])**
   for(j=1;j<=floor(log2(n));j++)
    for(i=1;i<=(n-(1<<j)+1);i++)
    {
    
    
         Fmin[i][j] = min( Fmin[i][j-1] , Fmin[i+1<<(j-1)][j-1] );
         Fmax[i][j] = max( Fmax[i][j-1] , Fmax[i+1<<(j-1)][j-1] );
    }

查询:

上例中要求区间[2,8]的最小值,就要把它分成[2,5]和[5,8]两个区间,因为这两个区间的最小值可以直接由f[2][2]和f[5][2]得到。

扩展到一般情况,就是把区间[a,b]分成两个长度为2k的区间[a,a+2k-1]和区间[b-2k+1,b](保证有f[i][j]对应)。
区间[1,8]的最小值=f[1][3] k=3
区间[2,8]的最小值=min(f[2][2],f[5][2]); k=2
k=floor(log2(b-a+1)) //即2k=b-a+1
区间[a,b]的最小值MinA_B=min(f[a][k],f[b-2∧k+1][k])


k=floor(log2(b-a+1));
RMinQ[a][b] = min( Fmin[a][k] , Fmin[b-(1<<k) +1][k]  );
RMaxQ[a][b] = max( Fmax[a][k] , Fmax[b-(1<<k) +1][k]  );
//注意要加一,而且位运算优先级很低

例题代码

#include<bits/stdc++.h>
using namespace std;
int shu[50009];
int f[50009][20];
int ff[50009][20];
int n,m;

void slove()
{
    
    
	memset(f,-1,sizeof(f));
	memset(ff,0xf,sizeof(ff));
	for(int i=1;i<=n;i++)
	{
    
    
		f[i][0]=shu[i];
		ff[i][0]=shu[i];
	}
	int k=floor(log2(n));
	for(int j=1;j<=k;j++)
	for(int i=1;i<=(n-(1<<j)+1);i++)
	{
    
    
		f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
		ff[i][j]=min(ff[i][j-1],ff[i+(1<<(j-1))][j-1]);
	}
	return ;
}
void query(int l,int r)
{
    
    
	int k=floor(log2(r-l+1));
	int ans=max(f[l][k],f[r-(1<<k)+1][k]);
	ans=ans-min(ff[l][k],ff[r-(1<<k)+1][k]);
	printf("%d\n",ans);
	return ;
}
int main()
{
    
    
	freopen("data.in","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&shu[i]);
	slove();
	int l,r;
	while(m--)
	{
    
    
		scanf("%d%d",&l,&r);
		query(l,r);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_50089378/article/details/109322316