有关st表的学习
关于这个知识点的学习的契机是25年东北邀请赛的a题
其中官方答案是二分加st表,但是也能用压缩加双指针;
有关st表
st表是一种稀疏表,第一个系数是起始点,第二个系数是当前区间的长度,能用O(1)的速度进行区间的有关量查询 代码如下:点击查看代码
////求最小开启
#define max min
////求最大公倍数开启
#define max __gcd
////求最大均不开启
void solve_st(){
int n;
cin>>n;
vector<int>a(n+1);
int max_len=(int)log2(n);
vector<vector<int>>st_(n+1,vector<int>(max_len+1));
////a[i][j]的意思是第i个元素开始,长度为2^j长度的闭区间数组的目标元素,简而言之就是区间[i,i+(1<<j)-1]的目标元素,这里是最大值;
////优先考虑数组长度
for(int j=0;j<=max_len;j++){
////起始点位置
for(int i=1;i+(1<<j)-1<=n;i++){
if(j==0)st_[i][j]=a[i];
else st_[i][j]=max(st_[i][j-1],st_[i+(1<<(j-1))][j-1]);
}
}
////查询[l,r]区间的目标值:
auto find_st=[&](int l,int r){
int len=r-l+1;
len=(int)log2(len);
return max(st_[l][len],st_[r-(1<<len)+1][len]);
};
}
1.对于st表的创建:
对于每一个开始的节点,一个大的区间的目标值等于【左端点和大区间的左端点重合,长度小于最大区间的最大区间】和【右端点和大区间的右端点重合,长度小于最大区间的最大区间】两个区间的目标值的目标值,其中长度为1的区间目标值为其本身,之后从小区间扩展到大区间。
2.对于st表区间的查询
取两个区间分别是左端点是l长度为2的幂次方的最长区间的值和右端点为r长度为2的幂次方的最长区间的值,之后直接求出两个目标值最优的值作为返回值。
对于上面那个a题的ac代码:
点击查看代码
void solve1(){
int n;
cin>>n;
vector<int>a(n+1);
for(int i=1;i<=n;i++)cin>>a[i];
int maxlen=(int)log2(n);
vector<vector<int>>gcds(n+1,vector<int>(maxlen+1));
vector<vector<int>>mins(n+1,vector<int>(maxlen+1));
for(int j=0;j<=maxlen;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
if(j==0)mins[i][j]=a[i],gcds[i][j]=a[i];
else mins[i][j]=min(mins[i][j-1],mins[i+(1<<(j-1))][j-1]),gcds[i][j]=__gcd(gcds[i][j-1],gcds[i+(1<<(j-1))][j-1]);
}
}
auto chamin=[&](int l,int r){
int len=r-l+1;
len=(int)log2(len);
return min(mins[l][len],mins[r-(1<<len)+1][len]);
};
auto chagcd=[&](int l,int r){
int len=r-l+1;
len=(int)log2(len);
return __gcd(gcds[l][len],gcds[r-(1<<len)+1][len]);
};
ll ans=0;
for(int i=1;i<=n;i++){
int l=i,l1=0;
int mid;
while(l-l1!=1&&i!=1){
mid=(l+l1)/2;
////左边取大于当前值的元素
if(chamin(mid,i-1)>a[i]&&__gcd(chagcd(mid,i-1),a[i])==a[i]){
l=mid;
}else{
l1=mid;
}
}
int r=i,r1=n+1;
////右边取大于等于当前值的元素
while(r1-r!=1&&i!=n){
mid=(r+r1)/2;
if(__gcd(chagcd(i+1,mid),a[i])==a[i]){
r=mid;
}else {
r1=mid;
}
}
ans+=(1LL*(i-l+1)*(r-i+1));
}
cout<<ans<<endl;
}
总结:st表作为稀疏表能快速多次查询区间目标值