ST表poj3264

时间:2024-04-18 17:35:31
 
/*
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[][maxn],p[maxn],f[][maxn];
int init()
{
int x=;
int f=;
char s;
s=getchar();
while(s<''||s>'')
{
if(s=='-')f=;
s=getchar();
}
while(s>=''&&s<='')
{
x=x*+s-'';
s=getchar();
}
if(f==)return x;
else return -x;
}
void slove()
{
int i,j;
for(i=;i<=n;i++)
{
g[][i]=a[i];
f[][i]=a[i];
}
for(i=;i<=;i++)
for(j=;j+(<<i>>)<=n;j++)
g[i][j]=min(g[i-][j],g[i-][j+(<<i>>)]);
for(i=;i<=;i++)
for(j=;j+(<<i>>)<=n;j++)
f[i][j]=max(f[i-][j],f[i-][j+(<<i>>)]);
memset(p,-,sizeof(p));
for(i=;i<;i++)
p[<<i]=i;
for(i=;i<maxn;i++)
if(p[i]==-)
p[i]=p[i-];
}
int find1(int l,int r)
{
int w=p[r-l+];
return max(f[w][l],f[w][r-(<<w)+]);
}
int find2(int l,int r)
{
int w=p[r-l+];
return min(g[w][l],g[w][r-(<<w)+]);
}
int main()
{
n=init();m=init();
int i,l,r;
for(i=;i<=n;i++)
a[i]=init();
slove();
for(i=;i<=m;i++)
{
l=init();r=init();
if(l>r)swap(l,r);
cout<<find1(l,r)-find2(l,r)<<endl;
}
return ;
}