题目大意:
题目链接:http://poj.org/problem?id=3264
给出一个长度为的数列,求第位到第位的最大值减最小值。
思路:
RMQ做法:https://blog.csdn.net/SSL_ZYC/article/details/80422589
这道题也可以用分块做。当然线段树主席树也都可以。
简直比分块模板还简单。因为没有修改操作。
要求到之间的最小值,那么就先将这个数列分成块,每块初始化出一个最大值和最小值,时间复杂度
接着对于每次询问,就用分块求出询问区间的最大值和最小值,再两者相减,即为答案。
代码:
此代码在POJ上可以AC,但洛谷只能拿40分,应该是编译环境的问题。
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#define N 50100
using namespace std;
int n,m,a[N],t,x,y,L[N],R[N],pos[N],maxn[N],minn[N];
int find_max(int l,int r) //暴力求l到r之间的最大值
{
int ans=0;
for (int i=l;i<=r;i++)
ans=max(ans,a[i]);
return ans;
}
int find_min(int l,int r) //暴力求l到r之间的最小值
{
int ans=1e9;
for (int i=l;i<=r;i++)
ans=min(ans,a[i]);
return ans;
}
int Max(int l,int r) //求询问区间的最大值
{
int q=pos[l],p=pos[r];
if (q==p) return find_max(l,r);
int ans=max(find_max(l,R[q]),find_max(L[p],r));
for (int i=q+1;i<p;i++)
ans=max(ans,maxn[i]);
return ans;
}
int Min(int l,int r) //求询问区间的最小值
{
int q=pos[l],p=pos[r];
if (q==p) return find_min(l,r);
int ans=min(find_min(l,R[q]),find_min(L[p],r));
for (int i=q+1;i<p;i++)
ans=min(ans,minn[i]);
return ans;
}
int ask(int l,int r)
{
return Max(l,r)-Min(l,r);
}
int main()
{
memset(minn,0x3f3f3f3f,sizeof(minn));
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
t=(int)sqrt(n);
for (int i=1;i<=t;i++)
{
L[i]=R[i-1]+1;
R[i]=i*t;
}
if (R[t]<n)
{
L[++t]=R[t-1]+1;
R[t]=n;
}
for (int i=1;i<=t;i++)
for (int j=L[i];j<=R[i];j++)
{
pos[j]=i;
maxn[i]=max(maxn[i],a[j]);
minn[i]=min(minn[i],a[j]); //初始化每个区间的最大值和最小值
}
while (m--)
{
scanf("%d%d",&x,&y);
printf("%d\n",ask(x,y));
}
return 0;
}