模板 ST表
ST表,适用于解决RMQ(区间最值问题),类似于线段树和树状数组这两个算法
ST表相比于线段树,预处理的复杂度与线段树一样,而查询的复杂度则大大快于线段树
预处理 | 查询 | |
ST表 | O(nlogn) | O(1) |
线段树 | O(nlogn) | O(logn) |
ST表:
线段树(不开O2):
线段树(开O2优化):
经比较,我们可以轻而易举的看出,当我们解决RMQ问题时,ST表的速度明显优于线段树
ST表的主体是一个二维数组:st[i][j]。表示查询的数组的下标i到i+2^j-1
如何进行预处理呢??
首先,我们把从0~n-1的2^0的部分进行覆盖
然后向下处理
Q:处理??怎么处理??
A:不明白??那我们来举一个例子
我们以一个长度为5的数组
2^0部分覆盖过去就是 a[1],a[2],a[3],a[4],a[5]
2^1部分的长度为4,也就是下标从1到4,
st[0][1]是下标为0~1的区间的最值
也就是st[0][0]和st[0][1]的最值
以此往下类推
结论:st[i][j] = min ( st[i][j-1] , st[i + 2^(j-1) ][j-1] );
预处理函数(求最大值):
inline void init(int n) { for(int i=1;i<=n;i++) { st[i][0]=a[i]; } for(int j=1;(1<<j)<=n;j++) { for(int i=1;i+(1<<j)-1<=n;i++) { st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]); } } }
查询函数,这个地方就很难理解了
首先,初始化,初始化的时候,每一个状态对应的区间都是2^j
由于查询时的长度不知道
所以我们要用到一个定理:2^log(a)>a/2
因为log(a)函数表示是小于等于2的最大次方
也就是说,a>=2^(max_j)
其中2^max_j表示的是不大于a的2的最大次方
Q:不明白。。
A:没关系,那我们来看几个例子就知道了
log(4)=2; log(5~7)=2; log(8)=3; log(9~15)=3; log(16)=4; log(17~31)=4;....
Q:可窝还是不明白。
A:没关系,我们再以log(4)=2,log(5~7)=2为例
这些数的log都是2
再仔细琢磨一下log()里面的数
4,5,6,7。floor(sqre(4)),floor(sqre(5)),floor(sqre(6)),floor(sqre(7))它们的值都等于2。
也就是2^max<=a。
那么我们要查询x到y的最大值
我们设len=(y-x+1),data=log(len)=log(y-x+1)
根据上面的定理,我们可以知道,2^data>len/2
从下标(位置)上来看,x+2^data>(x+y)/2;
也就是越过了区间[x,y]的中间
因为位置超过了一半
所以x到y的最大值就可以表示为max(从x往后的2^t的最大值,从y往前2^t的最大值);
前面的状态我们可以表示为 st[data][x]
设后面的初始位置为k,也就是说,从y往前2^t的最大值的初始位置是k
那么k+2^data-1=y;
所以k=y-2^data+1;
所以后面的状态就是st[data][y-2^data+1]
所以说,区间[x,y]的最大值就可以表示为max(st[data][x],st[data][y-2^t+1])
所以查询的时间复杂度是O(1)
查询函数:
inline int search(int l,int r) { int k=(int)(log((double)(r-l+1))/log(2.0)); return max(st[l][k],st[r-(1<<k)+1][k]); }
一道ST表模板题,(上文也有提到)
可以轻而易举的看出ST表和线段树在处理RMQ问题上的速度差异
以下是AC代码(ST表和线段树都有 线段树的并非AC代码)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<ctype.h> using namespace std; int a[100001]; int st[100100][21]; inline int read() { int s=1, w=0; char ch=getchar(); for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1; for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0'; return s*w; } inline void init(int n) { for(int i=1;i<=n;i++) { st[i][0]=a[i]; } for(int j=1;(1<<j)<=n;j++) { for(int i=1;i+(1<<j)-1<=n;i++) { st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]); } } } inline int search(int l,int r) { int k=(int)(log((double)(r-l+1))/log(2.0)); return max(st[l][k],st[r-(1<<k)+1][k]); } int main() { int n,m; n=read(); m=read(); for(int i=1;i<=n;i++) { a[i]=read(); } init(n); for(int i=1;i<=m;i++) { int l,r; l=read(); r=read(); printf("%d\n",search(l,r)); } return 0; } // //#include<iostream> //#include<cstdio> //#include<cstring> //#include<cmath> //#include<ctype.h> //#define int long long int // //using namespace std; // //struct node //{ // int l; // int r; // int w; // int lazy; // int mxx; //}tree[5000010]; // //int n,m; //int ans; //int x,y; // //inline int read() //{ // int s=1, w=0; char ch=getchar(); // for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1; // for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0'; // return s*w; //} // //void build(int l,int r,int k) //{ // tree[k].l=l; // tree[k].r=r; // if(tree[k].l==tree[k].r) // { // tree[k].w=read(); // tree[k].mxx=tree[k].w; // return; // } // int m=(tree[k].l+tree[k].r)/2; // build(l,m,k*2); // build(m+1,r,k*2+1); // tree[k].w=tree[k*2].w+tree[k*2+1].w; // tree[k].mxx=max(tree[k*2].mxx,tree[k*2+1].mxx); //} // //inline void down(int k) //{ // tree[k*2].lazy+=tree[k].lazy; // tree[k*2+1].lazy+=tree[k].lazy; // tree[k*2].w+=tree[k].lazy*(tree[k*2].r-tree[k*2].l+1); // tree[k*2+1].w+=tree[k].lazy*(tree[k*2+1].r-tree[k*2+1].l+1); // tree[k].lazy=0; //} // //inline void ask_max_query(int k) //{ // if(tree[k].l>=x&tree[k].r<=y) // { // ans=max(ans,tree[k].mxx); // return; // } // if(tree[k].lazy) // { // down(k); // } // int m=(tree[k].l+tree[k].r)/2; // if(x<=m) // { // ask_max_query(k*2); // } // if(y>m) // { // ask_max_query(k*2+1); // } //} // //signed main() //{ // n=read(); // m=read(); // build(1,n,1); // for(int i=1;i<=m;i++) // { // x=read(); // y=read(); // ans=0; // ask_max_query(1); // cout<<ans<<endl; // } // return 0; //}
2019.11.15
考试前看了看代码,发现写的其实并不太对,线段树其实也是可以水过的
无需O2 简而易懂
/* 知识点:ST表 思路:ST表一开始讲的时候,没有认真听 导致ST表不会做,然后就上网搜了一下关于ST表的博客 基本的 */ //#include<iostream> //#include<cstdio> //#include<cstring> //#include<cmath> //#include<ctype.h> // //using namespace std; // //int a[100001]; //int st[100100][21]; // //inline int read() //{ // int s=1, w=0; char ch=getchar(); // for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1; // for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0'; // return s*w; //} // //inline void init(int n) //{ // for(int i=1;i<=n;i++) // { // st[i][0]=a[i]; // } // for(int j=1;(1<<j)<=n;j++) // { // for(int i=1;i+(1<<j)-1<=n;i++) // { // st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]); // } // } //} // //inline int search(int l,int r) //{ // int k=(int)(log((double)(r-l+1))/log(2.0)); // return max(st[l][k],st[r-(1<<k)+1][k]); //} // //int main() //{ // int n,m; // n=read(); // m=read(); // for(int i=1;i<=n;i++) // { // a[i]=read(); // } // init(n); // for(int i=1;i<=m;i++) // { // int l,r; // l=read(); // r=read(); // printf("%d\n",search(l,r)); // } // return 0; //} #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<ctype.h> #define int long long int using namespace std; struct node { int l; int r; int w; int lazy; int mxx; }tree[5000010]; int n,m; int ans; int x,y; inline int read() { int s=1, w=0; char ch=getchar(); for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1; for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0'; return s*w; } void build(int l,int r,int k) { tree[k].l=l; tree[k].r=r; if(tree[k].l==tree[k].r) { tree[k].w=read(); tree[k].mxx=tree[k].w; return; } int m=(tree[k].l+tree[k].r)/2; build(l,m,k*2); build(m+1,r,k*2+1); tree[k].w=tree[k*2].w+tree[k*2+1].w; tree[k].mxx=max(tree[k*2].mxx,tree[k*2+1].mxx); } inline void down(int k) { tree[k*2].lazy+=tree[k].lazy; tree[k*2+1].lazy+=tree[k].lazy; tree[k*2].w+=tree[k].lazy*(tree[k*2].r-tree[k*2].l+1); tree[k*2+1].w+=tree[k].lazy*(tree[k*2+1].r-tree[k*2+1].l+1); tree[k].lazy=0; } inline void ask_max_query(int k) { if(tree[k].l>=x&tree[k].r<=y) { ans=max(ans,tree[k].mxx); return; } if(tree[k].lazy) { down(k); } int m=(tree[k].l+tree[k].r)/2; if(x<=m) { ask_max_query(k*2); } if(y>m) { ask_max_query(k*2+1); } } signed main() { n=read(); m=read(); build(1,n,1); for(int i=1;i<=m;i++) { x=read(); y=read(); ans=0; ask_max_query(1); printf("%lld\n",ans); } return 0; }
祝自己 2019 CSP-S RP++!!!