链接: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