鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5289
Assignment
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 332 Accepted Submission(s): 169
題意:
問有多少區間段,最大小值差<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;
}