st表
学习了这篇📕,然后结合学长教的。
一、一维st表:
用st[i][j]表示从i开始,1 << j 个连续数的最值; 设我们求某一区间的最值,则可拆分成这个区间前半区间和后半区间的最值,然后再继续拆,就很logn了。 然后写的时候就先给st[i][0]赋a[i],然后从小区间到大区间循环,把st“neng”上去,注意是j套i(因为要确保判断的st要处理过,处理的st[i+(1<<(j-1))][j-1]的那一块,j套i在上一次就遍历过,如果i套j的话前面部分还没),还要判断i+(1<<(j-1))是否超过n,不然没意义了~ 所以预处理的复杂度是O(nlogn)
而询问也联系拆成一半的思路,还要知道一个(化简就出来)的定理:2^log(a)>a/2 。我们查 l - r 的最值,区间长度是 len=r-l+1,令 t = log(len) ,则 2^t>len/2 ;
所以可以想象得到 l + 2^t 超过了区间的一半 ,r - 2^t +1 也超过了一半, 这两部分合起来比较最值就得到了,所以询问的复杂度是O(1)!,想象一下veen图的形状~ 然后就是酱
1 int st[N][20],a[N]; 2 void findm() 3 { 4 for(int i=0;i<n;i++) {st[i][0]=a[i];} 5 for(int j=1;j<20;j++) 6 for(int i=0;i<n;i++) 7 if(i+(1<<(j-1))<n) 8 st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); 9 } 10 int query(int l,int r) 11 { 12 int len=log2(r-l+1); 13 return min(st[l][len],st[r-(1<<len)+1][len]); 14 }
🎈有一道板子题 poj-3264 Balanced Lineup //如果log2用不了,就写 floor(log10(r-l+1)/log10(2)) 好了
1 //#include<bits/stdc++.h> 2 #include<iostream> 3 #include<stack> 4 #include<algorithm> 5 #include<cstdio> 6 #include<cmath> 7 #include<cstring> 8 #define mem(a) memset(a,0,sizeof(a)) 9 #define ll long long 10 #define mp make_pair 11 #define inf 0x3f3f3f3f 12 const int N=2e5+5; 13 const int M=1e3+10; 14 const ll lim=1e14+5; 15 using namespace std; 16 int n,top=0,m; 17 int st[N][20],stmx[N][20],a[N]; 18 void findm() 19 { 20 for(int i=0;i<n;i++) {st[i][0]=a[i];stmx[i][0]=a[i];} 21 22 for(int j=1;j<20;j++) 23 for(int i=0;i<n;i++) 24 if(i+(1<<(j-1))<n) 25 st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); 26 27 for(int j=1;j<20;j++) 28 for(int i=0;i<n;i++) 29 if(i+(1<<(j-1))<n) 30 stmx[i][j]=max(stmx[i][j-1],stmx[i+(1<<(j-1))][j-1]); 31 32 } 33 int query(int l,int r) 34 { 35 int len=log2(r-l+1); 36 // cout<<"***"<<len; 37 int mna=min(st[l][len],st[r-(1<<len)+1][len]); 38 int mxa=max(stmx[l][len],stmx[r-(1<<len)+1][len]); 39 //cout<<"***"<<mxa<<"***"<<mna<<endl; 40 return mxa-mna; 41 } 42 int main() 43 { 44 scanf("%d%d",&n,&m); 45 for(int i=0;i<n;i++) 46 scanf("%d",&a[i]); 47 48 findm(); 49 int l,r; 50 while(m--) 51 { 52 53 scanf("%d%d",&l,&r); 54 printf("%d\n",query(l-1,r-1)); 55 } 56 return 0; 57 }
1 //#include<bits/stdc++.h> 2 #include<iostream> 3 #include<stack> 4 #include<algorithm> 5 #include<cstdio> 6 #include<cmath> 7 #include<cstring> 8 #define mem(a) memset(a,0,sizeof(a)) 9 #define memm(a) memset(a,inf,sizeof(a)) 10 #define ll long long 11 #define ld long double 12 #define uL unsigned long long 13 #define mp make_pair 14 #define pb push_back 15 #define inf 0x3f3f3f3f 16 using namespace std; 17 const int N=1e6+5; 18 const int M=100+5; 19 int n,s,k,x[N],y[N],z[N]; 20 int a[N],treemax[4*N],treemin[4*N],lazy[4*N]; 21 void push_up(int rt) 22 { 23 treemax[rt]=max(treemax[rt],max(treemax[rt<<1],treemax[rt<<1|1])); 24 treemin[rt]=min(treemin[rt],min(treemin[rt<<1],treemin[rt<<1|1])); 25 } 26 27 void build(int rt,int l,int r) 28 { 29 if(l==r) 30 { 31 treemax[rt]=max(a[l],treemax[rt]); 32 treemin[rt]=min(a[l],treemin[rt]); 33 return; 34 } 35 int mid=(l+r)>>1; 36 build(rt<<1,l,mid); 37 build(rt<<1|1,mid+1,r); 38 push_up(rt); 39 } 40 41 int querymax(int L,int R,int rt,int l,int r) 42 { 43 if(L<=l&&r<=R) return treemax[rt]; 44 int mid=(l+r)>>1; 45 int ans=0; 46 if(L<=mid) ans=max(ans,querymax(L,R,rt<<1,l,mid)); 47 if(R>mid) ans=max(ans,querymax(L,R,rt<<1|1,mid+1,r)); 48 return ans; 49 } 50 int querymin(int L,int R,int rt,int l,int r) 51 { 52 if(L<=l&&r<=R) return treemin[rt]; 53 int mid=(l+r)>>1; 54 int ans=inf; 55 if(L<=mid) ans=min(ans,querymin(L,R,rt<<1,l,mid)); 56 if(R>mid) ans=min(ans,querymin(L,R,rt<<1|1,mid+1,r)); 57 return ans; 58 } 59 int main() 60 { 61 scanf("%d%d",&n,&k); 62 for(int i=1;i<=n;i++) 63 scanf("%d",&a[i]); 64 for(int i=1;i<=4*n;i++) 65 treemax[i]=0,treemin[i]=inf; 66 build(1,1,n); 67 int l,r; 68 while(k--) 69 { 70 scanf("%d%d",&l,&r); 71 int mx=querymax(l,r,1,1,n),mn=querymin(l,r,1,1,n); 72 // cout<<"---"<<mx<<"---"<<mn<<endl; 73 printf("%d\n",mx-mn); 74 } 75 return 0; 76 }
二、二维st表:
跟一维思想差不多,就是变成了矩形,这样一个大矩形就拆分成四个小的矩形 (差不多这个样子),然后这四个取最值就好 --》 max(max(,),max(,))
//这个是三维的(正方形)
1 int stm[300][300][20],a[300][300]; 2 void findm() 3 { 4 for(int i=1;i<=n;i++) 5 for(int j=1;j<=n;j++) 6 stm[i][j][0]=stn[i][j][0]=a[i][j]; 7 for(int k=1;k<20;k++) 8 for(int i=1;i<=n;i++) 9 for(int j=1;j<=n;j++) 10 if((i+(1<<(k-1)))<=n && (j+(1<<(k-1)))<=n) 11 stm[i][j][k]=max(max(stm[i][j][k-1],stm[i+(1<<(k-1))][j+(1<<(k-1))][k-1]), 12 max(stm[i][j+(1<<(k-1))][k-1],stm[i+(1<<(k-1))][j][k-1])), 13 } 14 int query(int l,int r,int k) 15 { 16 int len=log2(k); //k >= 1<<len 17 return max(max(stm[l][r][len],stm[l+k-(1<<len)][r+k-(1<<len)][len]), 18 max(stm[l][r+k-(1<<len)][len],stm[l+k-(1<<len)][r][len])); 19 }
🎈二维板子题: poj-2019 Cornfields //正好2019欸,好巧~
1 //#include<bits/stdc++.h> 2 #include<iostream> 3 #include<stack> 4 #include<algorithm> 5 #include<cstdio> 6 #include<cmath> 7 #include<cstring> 8 #define mem(a) memset(a,0,sizeof(a)) 9 #define ll long long 10 #define mp make_pair 11 #define inf 0x3f3f3f3f 12 const int N=2e5+5; 13 const int M=1e3+10; 14 const ll lim=1e14+5; 15 using namespace std; 16 int n,top=0,m; 17 int stm[300][300][20],stn[300][300][20],a[300][300]; 18 void findm() 19 { 20 for(int i=1;i<=n;i++) 21 for(int j=1;j<=n;j++) 22 stm[i][j][0]=stn[i][j][0]=a[i][j]; 23 for(int k=1;k<20;k++) 24 for(int i=1;i<=n;i++) 25 for(int j=1;j<=n;j++) 26 if((i+(1<<(k-1)))<=n && (j+(1<<(k-1)))<=n) 27 stm[i][j][k]=max(max(stm[i][j][k-1],stm[i+(1<<(k-1))][j+(1<<(k-1))][k-1]), 28 max(stm[i][j+(1<<(k-1))][k-1],stm[i+(1<<(k-1))][j][k-1])), 29 stn[i][j][k]=min(min(stn[i][j][k-1],stn[i+(1<<(k-1))][j+(1<<(k-1))][k-1]), 30 min(stn[i][j+(1<<(k-1))][k-1],stn[i+(1<<(k-1))][j][k-1])); 31 } 32 int query(int l,int r,int k) 33 { 34 int len=log2(k); //k >= 1<<len 35 int mx=max(max(stm[l][r][len],stm[l+k-(1<<len)][r+k-(1<<len)][len]), 36 max(stm[l][r+k-(1<<len)][len],stm[l+k-(1<<len)][r][len])); 37 int mi=min(min(stn[l][r][len],stn[l+k-(1<<len)][r+k-(1<<len)][len]), 38 min(stn[l][r+k-(1<<len)][len],stn[l+k-(1<<len)][r][len])); 39 return mx-mi; 40 } 41 int main() 42 { 43 int l,r,k; 44 while(~scanf("%d%d%d",&n,&k,&m)){ 45 mem(stm);mem(stn); 46 for(int i=1;i<=n;i++) 47 for(int j=1;j<=n;j++) 48 scanf("%d",&a[i][j]); 49 findm(); 50 while(m--) 51 { 52 scanf("%d%d",&l,&r); 53 printf("%d\n",query(l,r,k)); 54 } 55 } 56 return 0; 57 }
还有一个四维的矩形题 hdoj-2888 Check Corners :
算是模板题了吧,思路还是一样的。这里n宽m长,k是从i开始宽为1<<k。关键是k和kk是从0开始,因为要考虑第一行和第一列的情况,然后判断k是否为0,为0则单独考虑第一行情况。st表示从左上角开始的矩形最大值。
//不晓得三维用一个个正方形比较哪里错了o(TヘTo) 。
1 #include<iostream> 2 #include<stack> 3 #include<algorithm> 4 #include<cstdio> 5 #include<cmath> 6 #include<cstring> 7 #define mem(a) memset(a,0,sizeof(a)) 8 #define ll long long 9 #define mp make_pair 10 #define inf 0x3f3f3f3f 11 const int N=3e2+5; 12 const int M=1e3+10; 13 const ll lim=1e14+5; 14 using namespace std; 15 int n,m,a[N][N],st[N][N][9][9],lens ; 16 void findm() 17 { 18 for(int k=0;(1<<k)<=n;k++) 19 for(int kk=0;(1<<kk)<=m;kk++) 20 { 21 if(k==0&&kk==0) continue; 22 for(int i=1;i<=n;i++) 23 for(int j=1;j<=m;j++) 24 if(i+(1<<(k-1))<=n&&j+(1<<(kk-1))<=m) 25 { 26 if(k) 27 st[i][j][k][kk]=max(st[i][j][k-1][kk],st[i+(1<<(k-1))][j][k-1][kk]); 28 else 29 st[i][j][k][kk]=max(st[i][j][k][kk-1],st[i][j+(1<<(kk-1))][k][kk-1]); } 30 } 31 32 } 33 int query(int x1,int y1,int x2,int y2) 34 { 35 int lenx=log2(x2-x1+1),leny=log2(y2-y1+1); 36 return max(max(st[x1][y1][lenx][leny],st[x2+1-(1<<lenx)][y2+1-(1<<leny)][lenx][leny]), 37 max(st[x2+1-(1<<lenx)][y1][lenx][leny],st[x1][y2+1-(1<<leny)][lenx][leny])); 38 39 } 40 int main() 41 { 42 while(~scanf("%d%d",&n,&m)) 43 { 44 for(int i=1;i<=n;i++) 45 for(int j=1;j<=m;j++) 46 {scanf("%d",&a[i][j]);st[i][j][0][0]=a[i][j];} 47 findm(); 48 int x1,y2,y1,x2,q; 49 scanf("%d",&q); 50 while(q--) 51 { 52 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 53 int ans=query(x1,y1,x2,y2); 54 if(ans==a[x1][y1]||ans==a[x1][y2]||ans==a[x2][y1]||ans==a[x2][y2]) 55 printf("%d yes\n",ans); 56 else printf("%d no\n",ans); 57 } 58 } 59 return 0; 60 }
三、题目
题意:求一段区间内出现次数最多的数(不过好像没说次数一样的//或者我看漏了~)因为数是从小到大排的,所以就比较简单了。之前分块入门9,呜呼。用st表的话,就要维护一个最值,这题求众数,那st数组就存自己从开始到现在(这个下标)出现了几次,然后取大就好了。但是因为是某一区间,所以就这样会有错误,要判断[l,r]前后会不会有跟边界的点一样的数,这样就再来一个数组co,存这个数出现的最右边的位置。如果a[l]=a[l-1],那就要判断r是否小于co[l],这样就分成两种。①这一区间内所有值都是a[l],这样直接r-l+1;②前半段是a[l],后半段是其他,那就先看后半段的最大值,最后跟co[l]-l+1比较大小。
1 //#include<bits/stdc++.h> 2 3 #include<iostream> 4 #include<stack> 5 #include<algorithm> 6 #include<cstdio> 7 #include<cmath> 8 #include<cstring> 9 #define mem(a) memset(a,0,sizeof(a)) 10 #define ll long long 11 #define mp make_pair 12 #define inf 0x3f3f3f3f 13 const int N=1e5+5; 14 const int M=1e3+10; 15 const ll lim=1e14+5; 16 using namespace std; 17 int n,top=0,m; 18 int st[N][20],co[N],a[N]; 19 20 void findm() 21 { 22 int id=1; 23 a[0]=-N,co[n]=n; 24 for(int i=1;i<=n;i++) 25 { 26 if(a[i]!=a[i-1]) 27 { 28 id=1; 29 st[i][0]=id; 30 } 31 else 32 { 33 st[i][0]=++id; 34 } 35 } 36 for(int i=n-1;i>=1;i--) 37 { 38 if(a[i]==a[i+1]) co[i]=co[i+1]; 39 else co[i]=i; 40 } 41 // for(int i=1;i<=n;i++) 42 // cout<<"**"<<st[i][0]<<endl; 43 for(int j=1;j<20;j++) 44 for(int i=1;i<=n;i++) 45 if(i+(1<<(j-1))<=n) 46 { 47 st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]); 48 // cout<<"st["<<i<<"]["<<j<<"]= "<<st[i][j]<<endl; 49 } 50 51 } 52 int query(int l,int r) 53 { 54 int ans=0,k=l; 55 if(co[l]==co[l-1]) k=min(co[l],r)+1; 56 int len=log2(r-k+1); 57 if(k<=r) 58 ans=max(st[k][len],st[r-(1<<len)+1][len]); 59 return max(ans,k-l); 60 } 61 int main() 62 { 63 int l,r; 64 while(~scanf("%d%d",&n,&m)&&n){ 65 mem(st); 66 for(int i=1;i<=n;i++) 67 scanf("%d",&a[i]); 68 findm(); 69 while(m--) 70 { 71 scanf("%d%d",&l,&r); 72 printf("%d\n",query(l,r)); 73 } 74 } 75 return 0; 76 }