标签:rmq size 就是 log https com 文章 root 分享
\(RMQ(Range Minimum/Maximum Query)\)问题是指:对于长度为\(n\)的数列\(A\),回答若干询问\(RMQ(A,i,j)(i,j<=n)\),返回数列\(A\)中下标在\(i\),\(j\)里的最小(大)值,也就是说,\(RMQ\)问题是指求区间最值的问题。
本文所采用的例题Luogu P3865。
题目所述“请注意最大数据时限只有0.8s,数据强度不低,请务必保证你的每次查询复杂度为O(1)”,而这里却不止一种解法。
对于每个询问,从\(A_i\)扫到\(A_j\),途中记录最大值即可,这里就不贴Code了。
将原序列\(A\)分割成许多“块”,并由\(pos_i\)标记\(A_i\)位于的块序号,由\(Max_i\)来管理第\(i\)块的最大值。对于每个询问:
代码:
//[Template]Sqrt Block
//Luogu P3865 【模板】ST表
//https://www.luogu.org/problemnew/show/P3865
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e6+10;
const int M=1e6+10;
const int SQRTN=1e3*2;
const int size=1000;
int n,m;
int A[N];
int Max[SQRTN];
int pos[N];
inline int Query_max(int l,int r)
{
register int i;int res=-0x3f3f3f3f;
if(pos[r]==pos[l])
{
for(i=l;i<=r;i++)
res=max(res,A[i]);
return res;
}
for(i=l;i<=pos[l]*size;i++)
res=max(res,A[i]);
for(i=pos[l]+1;i<=pos[r]-1;i++)
res=max(res,Max[i]);
for(i=(pos[r]-1)*size+1;i<=r;i++)
res=max(res,A[i]);
return res;
}
int main()
{
register int i;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&A[i]),pos[i]=(i/size)+1;
for(i=1;i<=n;i++)
Max[pos[i]]=max(Max[pos[i]],A[i]);
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",Query_max(l,r));
}
return 0;
}
效率略逊于BIT等数据结构,可能会TLE,但比起朴素做法,也有较大优势。而且分块好理解,而且拓展性较高。
总的来说,比LG的P3372简单多了(不用\(lazy-tag\)了)。
每个节点维护最大值,但其实只要数组即可。
代码:
//[Template]Segment Tree
//Luogu P3865 【模板】ST表
//https://www.luogu.org/problemnew/show/P3865
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e6+10;
const int M=1e6+10;
const int ROOT=1;
int Max[4*N];
int A[N];
int n,m;
void Build(int rt,int l,int r)
{
if(l==r)
{
Max[rt]=A[l];
return;
}
Build(rt<<1,l,(l+r)>>1);
Build(rt<<1|1,((l+r)>>1)+1,r);
Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);
return;
}
int Query_max(int rt,int l,int r,int x,int y)
{
if(x<=l&&y>=r)return Max[rt];
if(x>r||y<l)return -0x3f3f3f3f;
return max(Query_max(rt<<1,l,(l+r)>>1,x,y),Query_max(rt<<1|1,((l+r)>>1)+1,r,x,y));
}
int main()
{
register int i;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&A[i]);
Build(ROOT,1,n);
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",Query_max(ROOT,1,n,l,r));
}
return 0;
}
理解以后,线段树查询的用途几乎是最广的。
BIT的空间、代码长度都比线段树更优。但是比较难理解的。比如:
显然,它的空间省了许多而且更快。
//[Template]Binary Index Tree
//Luogu P3865 【模板】ST表
//https://www.luogu.org/problemnew/show/P3865
#include<cstdio>
#include<iostream>
#define lowbit(x) (x&-x)
using namespace std;
const int N=1e6;
const int M=1e6;
int Max[N];
int n,cnt,temp,m;
int a[N];
inline void Add(int x)
{
int low, i;
while (x<=n)
{
Max[x]=a[x];
low=lowbit(x);
for (i=1;i<low;i<<=1)
Max[x]=max(Max[x],Max[x-i]);
x+=lowbit(x);
}
}
int Query_max(int x, int y)
{
int ans=0;
while (y>=x)
{
ans=max(a[y], ans);
y--;
while(y-lowbit(y)>=x)
{
ans=max(Max[y],ans);
y-=lowbit(y);
}
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(register int i=1;i<=n;i++)
scanf("%d",&a[i]),Add(i);
for(register int i=1,x,y;i<=m;i++)
{
scanf("%d%d",&x,&y);
printf("%d\n",Query_max(x,y));
}
return 0;
}
线段树虽然在许多方面逊于BIT,但之所以有线段树的存在,是因为线段树能适用于很多方面,不仅仅是区间、单点的查询修改,还有标记等等,可以用于模拟、DP等等。
参考:https://tjor.blog.luogu.org/xian-duan-shu-yu-shu-zhuang-shuo-zu
ST表是一种初始化\(O(nlogn)\),而查询只要\(O(1)\)的高效算法。
我们用\(Max[i][j]\)表示,从\(i\)位置开始的\(2^j\)个数中的最大值,例如\(Max[i][1]\)表示的是\(i\)位置和\(i+1\)位置中两个数的最大值
那么转移的时候我们可以把当前区间拆成两个区间并分别取最大值(注意这里的编号是从\(1\)开始的)
查询的时候也比较简单
我们计算出\(log_2{\text{区间长度}}\)
然后对于左端点和右端点分别进行查询,这样可以保证一定可以覆盖查询的区间:
代码:
//[Template]ST Table
//Luogu P3865 【模板】ST表
//https://www.luogu.org/problemnew/show/P3865
#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
const int N=1e6+10;
const int M=1e6+10;
int n,m;
int Max[N][21];//从i位置开始的2^j个数中的最大值
//a * (2^n) 等价于 a<< n
inline int Query_max(int l,int r)
{
int k=log2(r-l+1);
return max(Max[l][k],Max[r-(1<<k)+1][k]);
}
int main()
{
register int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&Max[i][0]);
for(j=1;j<=21;j++)
for(int i=1;i+(1<<j)-1<=n/*===>>[i+2^j-1<=N]*/;i++)
Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))/*i+2^(j-1)*/][j-1]);
for(i=1;i<=m;i++)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",Query_max(l,r));
}
return 0;
}
目前,最优的算法。
参考:http://www.cnblogs.com/zwfymqz/p/8581995.html
\(RMQ\)问题还可以有笛卡尔树
或莫队
的做法,这里就不再写了。(其实我不会)
暂时在此。
标签:rmq size 就是 log https com 文章 root 分享
原文地址:https://www.cnblogs.com/-Wallace-/p/10326333.html