链接:http://acm.hdu.edu.cn/showproblem.php?pid=5289
2 4 2 3 1 2 4 10 5 0 3 4 5 2 1 6 7 8 9
5 28HintFirst Sample, the satisfied groups include:[1,1]、[2,2]、[3,3]、[4,4] 、[2,3]
题意:
问有多少区间段,最大小值差<k。
做法:
枚举右端点,很明显 区间越大,最大小值差越大,所以有线性关系。所以可以二分。找到差值小于k的点,这个点到右端点之间所有点都可以做为左端点。
线段树和树状数组都可能超时,离线最大小值计算最稳的就是RMQ了。
#include<stdio.h> #include<string.h> #include<math.h> #include<iostream> #include<algorithm> using namespace std; const int MAXN = 100100; int n,query; int A[MAXN]; int FMin[MAXN][20],FMax[MAXN][20]; void Init(){ int i,j; for(i=1;i<=n;i++) FMin[i][0]=FMax[i][0]=A[i]; for(i=1;(1<<i)<=n;i++){ //按区间长度递增顺序递推 for(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(){ int a,b; int t; scanf("%d",&t); while(t--) { int k; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&A[i]); Init(); int lll=1; __int64 ans=0; for(int i=1;i<=n;i++) { int l=lll; int 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 if(tt<k) r=mid-1; } lll=l; ans+=i-l+1; } printf("%I64d\n",ans); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/u013532224/article/details/46992383