#10119. 「一本通 4.2 例 1」数列区间最大值 & P1816 忠诚(ST算法)
-
https://loj.ac/problem/10119 & https://www.luogu.org/problemnew/show/P1816
-
时间复杂度:O(nlogn)+O(m);(n为个数,m为问题数)
-
思路:详见信息学奥赛一本通;
-
预处理:ST算法实际是DP;a[MAXN]存数,f[i][j](f[MAXN][21])表示a[i]~a[i+2的j次方-1]范围内最大值(即a[i]为起点连续2的j次方个数的最大值);从中间平均分成两部分,每部分元素刚好2的j-1次方个,即,f[i][j]分为f[i][j-1]和f[i+2的j-1次方][j-1]。故得到状态转移方程f[i][j]=max(f[i][j-1],f[i+2的j-1次方][j-1]),边界条件f[i][0]=a[i]。
-
询问:询问[li,ri]间最大值(即max,最小值即min),先求出x满足2的x次方<=ri-li+1,那么区间[li,ri]=[li,li+2的x次方-1]∩[ri-2的x次方+1,ri],两个区间元素均为2的x次方个,所以[li,ri]最大值为max(f[li][x],f[ri-2的x次方+1][x])(∵有交集)。直接给表达式:k=log2(y-x+1);ans=max(f[x][k],f[y-2的k次方+1][k])。
-
技巧(待证明):log2函数效率低,故可用O(N)递推预处理出1~N这N种区间长度各自对应的k值,具体操作:lg[d]数组表示log2d向下取整,则lg[d]=lg[d/2]+1;
-
代码:最大值max最小值min
#include<bits/stdc++.h> using namespace std; const int MAXN=1e6+2; int n,m; int a[MAXN],lg[MAXN],f[MAXN][21]; int main(){ cin>>n>>m; for(int i=1;i<=n;i++) scanf("%d",&a[i]); //存数 lg[0]=-1; for(int i=1;i<=n;i++) f[i][0]=a[i],lg[i]=lg[i>>1]+1; //边界处理+技巧lg数组 for(int i=1;i<=20;i++) //一般最大到2的20次方即可 for(int j=1;j+(1<<i)-1<=n;j++) //j+(1<<i)-1<=n即区间边界不可超过n;1<<i即2的i次方 f[j][i]=max(f[j][i-1],f[j+(1<<i-1)][i-1]); //状态转移方程f[i][j]=max(f[i][j-1],f[i+2的j-1次方][j-1]) int x,y; for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); int s=lg[y-x+1]; //技巧lg数组 printf("%d\n",max(f[x][s],f[y-(1<<s)+1][s])); //max(f[li][x],f[ri-2的x次方+1][x])(∵有交集) } return 0; }