第12期:ST表

参考资料:

ST表算法详解

ST表详解+模板

ST表 「 从入门到入门 · 浅显理解 」

浅谈ST表

某个区间查询问题是否适用ST表,关键在于其进行的操作是否允许区间重叠,例如max(a,b,c) = max{max(a,b),max(b,c)}就可以用ST表维护,而区间和问题则不能维护。

问题描述
给定一个长度为n的序列,有m次询问,每次给定区间[L , R],求区间内最大值。
算法思路
定义st表
我们设 st[i][j] 为从 i 开始的向后 2^j 个数中的最大值。假设这n个数存放的序列a中,根据定义 st[i][j] = max{a[k] | i <= k <= i+2^j - 1}。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1e6+10;
inline int read()
{
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int Max[MAXN][21];
int Query(int l,int r)
{
    int k=log2(r-l+1); 
    return max(Max[l][k],Max[r-(1<<k)+1][k]);//把拆出来的区间分别取最值 
}
int main()
{
    int N=read(),M=read();
    for(int i=1;i<=N;i++) Max[i][0]=read();
    for(int j=1;j<=21;j++)
        for(int i=1;i+(1<<j)-1<=N;i++)//注意这里要控制边界 
            Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))][j-1]);//如果看不懂边界的话建议好好看看图 
    for(int i=1;i<=M;i++)
    {
        int l=read(),r=read();
        printf("%d\n",Query(l,r));
    }
    return 0;
}

例题

1 P3865 【模板】ST 表

//ST表模板解法 
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1e6+10;
inline int read()
{
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int Max[MAXN][21];
int Query(int l,int r)
{
    int k=log2(r-l+1); 
    return max(Max[l][k],Max[r-(1<<k)+1][k]);//把拆出来的区间分别取最值 
}
int main()
{
    int N=read(),M=read();
    for(int i=1;i<=N;i++) Max[i][0]=read();
    for(int j=1;j<=21;j++)
        for(int i=1;i+(1<<j)-1<=N;i++)//注意这里要控制边界 
            Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))][j-1]);//如果看不懂边界的话建议好好看看图 
    for(int i=1;i<=M;i++)
    {
        int l=read(),r=read();
        printf("%d\n",Query(l,r));
    }
    return 0;
}

2 P2251 质量检测

//ST表模板解法 167ms /  10.42MB /  798B C++14 (GCC 9)
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1e6+10;
inline int read()
{
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int Min[MAXN][21];
int Query(int l,int r)
{
    int k=log2(r-l+1); 
    return min(Min[l][k],Min[r-(1<<k)+1][k]);//把拆出来的区间分别取最值 
}
int main()
{
    int N=read(),M=read();
    for(int i=1;i<=N;i++) Min[i][0]=read();
    for(int j=1;j<=21;j++)
        for(int i=1;i+(1<<j)-1<=N;i++)//注意这里要控制边界 
            Min[i][j]=min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]);//如果看不懂边界的话建议好好看看图 
    for(int i=1;i<=N-M+1;i++)
    {
        int l=i,r=i+M-1;
        printf("%d\n",Query(l,r));
    }
    return 0;
}
//更快解法  78ms /  4.85MB /  602B C++20
#include<bits/stdc++.h>
#define For(i,a,b) for (int i=(a);i<=(b);i++)
using namespace  std;
inline int read()
{
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
const int N=2e6+10;
int n,k;
int ar[N],s[N],head,top;

int main()
{
	head=0,top=-1;
	n=read();k=read();
	For(i,1,n) ar[i]=read();
	
	For(i,1,n)
	{
		if (head<=top && i-k>=s[head])head++;
		while(head<=top && ar[i]<=ar[s[top]])top--;
		s[++top]=i;
		if (i>=k)printf("%d\n",ar[s[head]]);
	}
	
	return 0;
}

3 P7974 [KSN2021] Delivering Balls 

题解

#include<bits/stdc++.h>
#define he(i,j) (h[st[i][j]]+st[i][j])
#define eh(i,j) (h[ts[i][j]]+n-ts[i][j])
#define hh(i,j) (h[tt[i][j]])
#define ll long long 
using namespace std;
ll st[300000][20];
ll ts[300000][20];
ll tt[300000][20];
ll d[300000];
ll h[300000];
ll n;
ll maxnn(ll a,ll b){
	if(a==b)return ts[a][0];
	ll le=b-a+1;ll o=(b-(1<<d[le]))+1;
	if(he(a,d[le])>he(o,d[le])){
		return st[a][d[le]];
	}else return st[o][d[le]];
}
ll maxn(ll a,ll b){
	if(a==b)return st[a][0];
	ll le=b-a+1;ll o=b-(1<<d[le])+1;
	if(eh(a,d[le])>eh(o,d[le])){
		return ts[a][d[le]];
	}else return ts[o][d[le]];
}
ll maxt(ll a,ll b){
	if(a==b)return tt[a][0];
	ll le=b-a+1;ll o=b-(1<<d[le])+1;
	if(hh(a,d[le])>hh(o,d[le])){
		return tt[a][d[le]];
	}else return tt[o][d[le]];
}
ll len(ll a,ll b,ll f){//这里我是直接分别求这四段的路径长度 
	if(a==b)return 0;//和题解不同 
	ll ans=0;
	ans+=(b-a)*2;
	if(h[a]<h[b]){
		ans+=(h[b]-h[a])*(2-3*f);
		if(h[b]-h[a]>b-a)ans+=(h[b]-h[a]-(b-a))*1LL*2;
	}else{
		ans+=(h[a]-h[b])*(-1+3*f);
		if(h[a]-h[b]>b-a)ans+=(h[a]-h[b]-(b-a))*1LL*2;	
	} 
	return ans;
}
int main(){
	ll q;
	cin>>n;
	for(ll i=1;i<=n;i++){
		cin>>h[i];
		st[i][0]=ts[i][0]=tt[i][0]=i;
	}
	for(ll i=1;i<=19;i++){//三个ST表 
		ll le=1<<(i-1);
		for(ll j=1;j+le*2-1<=n;j++){
			if(he(j,i-1)>he(j+le,i-1)){
				st[j][i]=st[j][i-1];
			}else st[j][i]=st[j+le][i-1];
			if(eh(j,i-1)>eh(j+le,i-1)){
				ts[j][i]=ts[j][i-1];
			}else ts[j][i]=ts[j+le][i-1];
			if(hh(j,i-1)>hh(j+le,i-1)){
				tt[j][i]=tt[j][i-1];
			}else tt[j][i]=tt[j+le][i-1];
		}
	}
	ll le=0,g=1;
	for(ll i=1;i<=n;i++){//预处理对数 
		if(2*g<i){
			le++;g*=2;
		}d[i]=le;
	}cin>>q;
	for(ll i=1;i<=q;i++){
		ll a,b;cin>>a>>b;
		ll lo=0;
		if(a>b){
			swap(a,b);lo=1;
		}
		ll yg=maxnn(a,b),zg=maxn(a,b);
		ll zz=maxt(zg,yg);
	//	cout<<a<<" "<<zg<<" "<<zz<<" "<<yg<<" "<<b<<endl;
		ll ans=0;
		ans+=len(a,zg,lo);
		ans+=len(zg,zz,lo);
		ans+=len(zz,yg,lo);
		ans+=len(yg,b,lo);
		cout<<ans<<endl;
	}
	return 0;
} 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=200005;

int n,q,s,t;
int h[maxn];
int f[maxn][31],f1[maxn][31],f2[maxn][31],log_2[maxn];
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int main (){
    n=read();
    for(int i=1;i<=n;i++){
    	h[i]=read();
    	f[i][0]=h[i];
    	f1[i][0]=h[i]-i;
    	f2[i][0]=h[i]+i;
	}
	for(int i=1;i<=30;i++){
		for(int j=1;j+(1<<i)-1<=n;j++){
			f[j][i]=max(f[j][i-1],f[j+(1<<(i-1))][i-1]);
			f1[j][i]=max(f1[j][i-1],f1[j+(1<<(i-1))][i-1]);
			f2[j][i]=max(f2[j][i-1],f2[j+(1<<(i-1))][i-1]);
		}
	}
	for(int i=2;i<=n;i++){
		log_2[i]=log_2[i/2]+1; 
	}
	q=read();
	while(q--){
		int s,t;
		ll mh,ms,mt,ans,cnt;
		s=read();
		t=read();
		int l=s,r=t;
		if(l>r)swap(l,r);
		int len=log_2[r-l+1];
		
		mh=max(f[l][len],f[r-(1<<len)+1][len]);
		ms=max(f1[l][len],f1[r-(1<<len)+1][len]);
		mt=max(f2[l][len],f2[r-(1<<len)+1][len]);
		ms=ms-(h[l]-l);
		mt=mt-(h[r]+r);
		if(s>t)swap(ms,mt);
		cnt= abs(t-s)-((mh-ms-h[s]) + (mh-mt-h[t])); 
		ans = 4*(mh-h[s]) + (mh-h[t]) + 2*cnt; 
		printf("%lld\n",ans);
	}
    return 0;
}
/*
7
0 100 0 0 0 99 0
4
1 3
*/

4 P3295 [SCOI2016]萌萌哒 题解

5 P4501 [ZJOI2018]胖

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值