poj 3264
给n个数,求任意区间内的最值差。
思路: n l o g n nlogn nlogn 的 算法在这里可以过,有线段树和RMQ算法两种。这里介绍RMQ算法。
RMQ算法是位运算动态规划算法,定义dp[i][j] 为 [ i , i + 2 j − 1 ] [i,i+2^j -1] [i,i+2j−1] 区间内的最值。那么该区间可以划分为两个相同大小的区间 [ i , i + 2 j − 1 − 1 ] [i,i+2^{j-1}-1] [i,i+2j−1−1] 和 [ i + 2 j − 1 , i + 2 j − 1 ] [i+2^{j-1},i+2^j-1] [i+2j−1,i+2j−1] 。
这两个区间用dp数组表示就是 d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j−1], d p [ i + 2 j − 1 ] [ j − 1 ] dp[i+2^{j-1}][j-1] dp[i+2j−1][j−1] 。
所以状态转移方程:mm[i][j]=max/min(mm[i][j-1],mm[i+(1<<(j-1))][j-1]);
mm[i][0]=a[i];
查询[l,r]区间内的时候就找到一个k, ( i n t ) k = l o g 2 ( r − l + 1 ) (int)k=log_2(r-l+1) (int)k=log2(r−l+1) ,目的在于找到[l,r]的一个覆盖,两个区间覆盖[l,r],可以相交。这两个区间长度不超过r-l+1,并且是2的幂。这样子就可以用dp[l][k],dp[r-(1<<k)+1][k] 表示[l,r]区间内最值。
void rmq_init(int n)
{
for(int i=1; i<=n; i++)
{
mm[i][0]=mi[i][0]=a[i];
}
for(int j=1; (1<<j)<=n; j++)
{
for(int i=1; i+(1<<j)-1<=n; i++)
{
mm[i][j]=max(mm[i][j-1],mm[i+(1<<(j-1))][j-1]);
mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
}
}
}
void rmq(int l,int r)
{
int len=r-l+1;
int k=(int)(log(r-l+1.0)/log(2.0));
int ans1=max(mm[l][k],mm[r-(1<<k)+1][k]);
int ans2=min(mi[l][k],mi[r-(1<<k)+1][k]);
}
上面是模板。
对于这个题目来说模板是刚好的。注意的是不要用cin输入,会超时,哪怕使用ios::sync_with_stdio(false)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define maxn 50000+10
using namespace std;
int mm[maxn][100];
int mi[maxn][100];
int a[maxn];
void rmq_init(int n)
{
for(int i=1; i<=n; i++)
{
mm[i][0]=mi[i][0]=a[i];
}
for(int j=1; (1<<j)<=n; j++)
{
for(int i=1; i+(1<<j)-1<=n; i++)
{
mm[i][j]=max(mm[i][j-1],mm[i+(1<<(j-1))][j-1]);
mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
}
}
}
int rmq(int l,int r)
{
int len=r-l+1;
int k=(int)(log(r-l+1.0)/log(2.0));
int ans1=max(mm[l][k],mm[r-(1<<k)+1][k]);
int ans2=min(mi[l][k],mi[r-(1<<k)+1][k]);
return ans1-ans2;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)==2)
{
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
rmq_init(n);
int lef,righ;
for(int i=0; i<m; i++)
{
scanf("%d%d",&lef,&righ);
printf("%d\n",rmq(lef,righ));
}
}
return 0;
}