ST表poj3264

 
/*
ST表多次查询区间最小值
设 g[j][i] 表示从第 i 个数到第 i + 2 ^ j - 1 个数之间的最小值
类似DP的说 ans[i][j]=min (ans[i][mid],ans[mid+1][r])mid=(l+r)/2
but 数太大装不下 所以改一个g数组出来就好了
接下来考虑 g[i][j]由谁转移来(不漏下就好 因为是去min 可以重复 同理gcd也可以 其他的就要考虑考虑了)
解决方案是搞一个p[i] 表示长度为i的区间长度是2的p[i]次方(下取整) 
那么, ans[l][r]就可以不漏的表示为 min(g[w][l],g[w][r-(1<<w)+1])  (w=p[l-r+1])
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 200010
using namespace std;
int n,m,a[maxn],g[21][maxn],p[maxn],f[21][maxn];
int init()
{
    int x=0;
    int f=0;
    char s;
    s=getchar();
    while(s<'0'||s>'9')
      {
          if(s=='-')f=1;
          s=getchar();
      }
    while(s>='0'&&s<='9')
      {
          x=x*10+s-'0';
          s=getchar();
      }
    if(f==0)return x;
    else return -x;
}
void slove()
{
    int i,j;
    for(i=1;i<=n;i++)
      {
          g[0][i]=a[i];
          f[0][i]=a[i];
      }
    for(i=1;i<=18;i++)
      for(j=0;j+(1<<i>>1)<=n;j++)
        g[i][j]=min(g[i-1][j],g[i-1][j+(1<<i>>1)]);
    for(i=1;i<=18;i++)
      for(j=0;j+(1<<i>>1)<=n;j++)
        f[i][j]=max(f[i-1][j],f[i-1][j+(1<<i>>1)]);
    memset(p,-1,sizeof(p));
    for(i=0;i<18;i++)
      p[1<<i]=i;
    for(i=0;i<maxn;i++)
      if(p[i]==-1)
        p[i]=p[i-1];
}
int find1(int l,int r)
{
    int w=p[r-l+1];
    return max(f[w][l],f[w][r-(1<<w)+1]);
}
int find2(int l,int r)
{
    int w=p[r-l+1];
    return min(g[w][l],g[w][r-(1<<w)+1]);
}
int main()
{
    n=init();m=init();
    int i,l,r;
    for(i=1;i<=n;i++)
      a[i]=init();
    slove();
    for(i=1;i<=m;i++)
      {
          l=init();r=init();
          if(l>r)swap(l,r);
          cout<<find1(l,r)-find2(l,r)<<endl;
      }
    return 0;
}

 

 
posted @ 2016-05-04 17:36  一入OI深似海  阅读(136)  评论(0编辑  收藏  举报