题意:(看英文真费劲==) 给定一数列 求子区间能够满足其中最大值-最小值差<k 之前拿到题就想到线段树 确实数据小点儿能做 搜到了特别优雅的RMQ算法*^_^*
此算法用于求固定区间的最值 本质是动态规划和枚举
(一)首先是预处理,用动态规划(DP)解决。
设A[i]是要求区间最值的数列,F[i, j]表示从第i个数起连续2^j个数中的最大值。(DP的状态)
例如:
A数列为:3 2 4 5 6 8 1 2 9 7
F[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。同理 F[1,1] = max(3,2) = 3, F[1,2]=max(3,2,4,5) = 5,F[1,3] = max(3,2,4,5,6,8,1,2) = 8;
并且我们可以容易的看出F[i,0]就等于A[i]。(DP的初始值)
这样,DP的状态、初值都已经有了,剩下的就是状态转移方程。
我们把F[i,j]平均分成两段(因为f[i,j]一定是偶数个数字),从 i 到i + 2 ^ (j - 1) - 1为一段,i + 2 ^ (j - 1)到i + 2 ^ j - 1为一段(长度都为2 ^ (j - 1))。用上例说明,当i=1,j=3时就是3,2,4,5 和 6,8,1,2这两段。F[i,j]就是这两段各自最大值中的最大值。于是我们得到了状态转移方程F[i, j]=max(F[i,j-1], F[i + 2^(j-1),j-1])。
(二)然后是查询。
假如我们需要查询的区间为(i,j),那么我们需要找到覆盖这个闭区间(左边界取i,右边界取j)的最小幂(可以重复,比如查询5,6,7,8,9,我们可以查询5678和6789)。
因为这个区间的长度为j - i + 1,所以我们可以取k=log2( j - i + 1),则有:RMQ(A, i, j)=max{F[i , k], F[ j - 2 ^ k + 1, k]}。
举例说明,要求区间[2,8]的最大值,k = log2(8 - 2 + 1)= 2,即求max(F[2, 2],F[8 - 2 ^ 2 + 1, 2]) = max(F[2, 2],F[5, 2]);
在这里我们也需要注意一个地方,就是<<运算符和+-运算符的优先级。
本题:枚举右端点,很明显 区间越大,最大小值差越大,所以有线性关系。所以可以二分。找到差值小于k的点,这个点到右端点之间所有点都可以做为左端点。
#include <iostream> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int A[100100],FMIN[100100][20],FMAX[100100][20]; int t,n,k; void init() { for(int i=1;i<=n;i++) FMIN[i][0]=FMAX[i][0]=A[i]; for(int i=1;(1<<i)<=n;i++) for(int j=1;j+(1<<i)-1<=n;j++) { FMIN[j][i]=min(FMIN[j][i-1],FMIN[j+(1<<i-1)][i-1]); FMAX[j][i]=max(FMAX[j][i-1],FMAX[j+(1<<i-1)][i-1]); } } int query(int l,int r) { int k=(int)(log(double(r-l+1))/log((double)2)); return max(FMAX[l][k],FMAX[r-(1<<k)+1][k]); } int query2(int l,int r) { int k=(int)(log(double(r-l+1))/log((double)2)); return min(FMIN[l][k],FMIN[r-(1<<k)+1][k]); } int main() { // freopen("cin.txt","r",stdin); scanf("%d",&t); while(t--) { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&A[i]); init(); __int64 ans=0; int lll=1; for(int i=1;i<=n;i++) { int l=lll,r=i; while(l<=r) { int mid=(l+r)/2; int low=query2(mid,i); int hig=query(mid,i); int tt=hig-low; if(tt>=k) l=mid+1; else r=mid-1; } lll=l; ans+=i-l+1; } printf("%I64d\n",ans); } return 0; }