标签:提醒 https 访问 链接 元素 代码 记录 mes style
题目链接 https://www.luogu.org/problem/P3865
原文 https://www.luogu.org/blog/loi-syc/p3865-mu-ban-st-biao
这个题是很裸的st表,让我们先讲讲st表是个啥东西:
st表(RMQ)--- O(nlogn+Q)算法:
1.f[i][j] : 记录给定序列中区间[i,i+pow(2,j)-1]中的最大值。
起始条件:f[i][0]=a[i];
(根据上面的定义,f[i][0]代表[i,i]区间最小值(即a[i]:显然区间[i,i]里只有它一个数))。
2.bit[i] : bit[i]=pow(2,i)(记录2的i次方)。
起始条件:bit[0]=1,之后for循环生成即可。
3.a[i]:存给定序列。
4.LC:在生成st表时作为f[i][j]中j的循环上界。
在正式讲st表之前先看个模拟加深一下理解:
根据f[i][j]的定义,我们可以针对[1,5]模拟处理出所有长度为1,2,4的区间的最小值
长度为1的区间[1,1],[2,2],[3,3],[4,4],[5,5]
长度为2的区间[1,2],[2,3],[3,4],[4,5]
长度为4的区间[1,4],[2,5]
用f[i][j]表示:
E:长度为1的区间: f[1][0] <=>[1,1]
f[2][0] <=>[2,2]
E:长度为2的区间: f[1][1] <=>[1,2]
f[2][1] <=>[2,3]
f[1][2] <=>[1,4]
f[2][2] <=>[2,5]
总结一下:
1.在f[i][j]中,i为区间左端点,j决定了f[i][j]包括的比较过的的元素个数为2^j,区间右端点下标为i+pow(2,j)-1(注意这里有个-1);
2.由于我们循环生成st表时按区间长度由小到大,故先循环j(1~(int)log2(n))------ (此处的j是f[i][j]中的j)。
下面进入正题:
f[i][j]:从i出发算上i连续pow(2,j)个数的最大值
<=>从i出发连续pow(2,j-1) 与 从i+pow(2,j-1)出发连续pow(2,j-1)这两个最大值的最大值(分成两半)
方程:
f[i][j]=max(f[i][j-1],f[i+pow(2,j-1)][j-1]);
//1.生成st表(nlogn):
```cpp
LG=(int)(log(n)/log(2));//自动向下取整(显然向上取整搞st表时比较最大值时容易把给定序列外头的元素也包括在内,不过这对于最大值么什么大碍,如果是查找最小值。。。你就杯具了)
//log(n)/log(2)<=>log2(n)---换底公式
//换底公式:loga b=lg b/lg a
for (i=1; i<=LG; i++)
bit[i]=bit[i-1]*2;//初始化bit[i]
bit[0]=1;
for (i=1; i<=n; i++)
f[i][0]=a[i];//起始化
for (j=1; j<=LG; j++)//由于2^j为区间长度,故循环j其实就是把区间长度从1枚举到n(有可能<)
for (i=1; i<=n-bit[j]+1; i++)//对于每一个区间长度枚举所有可行的左端点
//i<=n-bit[j]+1------n-bit[j]+1+2^j-1=n
f[i][j]=max(f[i][j-1],f[i+bit[j-1]][j-1]);//方程
讲完了生成,再来个模拟大概理解一下st表的生成:
给定一个数列2 4 3 5 1:
此时LC=2,n=5;
f[1][0]=2 f[2][0]=4 f[3][0]=3 f[4][0]=5 f[5][0]=1
当j=1时, i = 1 to 4
f[1][1]=max(f[1][0],f[2][0])=4;[1,2]
f[2][1]=4
f[3][1]=5
f[4][1]=5
当j=2时, i = 1 to n-bit[j]+1=2
f[1][2]=max(f[1][1],f[3][1])=5;
f[2][2]=5
由于我们不能保证r就=l+2^x-1(x任意),显然我们应该让以l为起点的st表数据覆盖[l,r]中数足够多,
规定在f[i][j]中,i=l,j=p,len=r-l+1(区间长度);
找最大的p应满足2^p <= len(以l为起点的st表数据覆盖[l,r]中数足够多)
则p=int(log(len)/log(2))(再次提醒:向上取整会越界);
example:对于区间[4,8],p=2,分到[4,4+2^2-1],即[4,7];
因为f[l]p中最大的p满足l+pow(2,p)-1<=r
故查询时分的查询区间[l,r]的右区间的左端点x=r+1-pow(2,p)
对于f[x][r],应有:[x][x+pow(2,p)-1]<=>[x][r]
所以, x+pow(2,p)-1=r
移项,得:x=r+1-pow(2,p)
显然,x>=l (l+pow(2,p)-1<=r,x+pow(2,p)-1=r,故x>=l)
综上[l,r]的最大值 = max( f[l][p] , f[x][R] );
代码:
cin>>Q;
for (i=1;i<=Q;i++)
{
cin>>L>>R;
int len=R-L+1;
int p=(int)(log(len)/log(2));
x=R-bit[p]+1;
cout<<max(f[L][p],f[x][p])<<endl;
}
针对这个板子,完整代码如下(之前的bit[i]统一改成位运算<注意位运算与加减乘除优先级>):
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int f[100001][40],a,x,LC,n,m,p,len,l,r;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
f[i][0]=a;
}
LC=(int)(log(n)/log(2));
for(int j=1;j<=LC;j++)
for (int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&l,&r);
p=(int)(log(r-l+1)/log(2));
printf("%d\n",max(f[l][p],f[r-(1<<p)+1][p]));
}
return 0;
}
标签:提醒 https 访问 链接 元素 代码 记录 mes style
原文地址:https://www.cnblogs.com/aprincess/p/11632042.html