HDU 5289 :http://acm.hdu.edu.cn/showproblem.php?pid=3874
题目大意: N个数,每一次询问,会给一个区间 [L,R],要求这个区间内的最大数与最小数的差值小于给定的k,问,满足条件的区间数为多少个。有M次询问。
使用ST来解决题目中的,最大数和最小数差的RMQ问题。
对于左区间L,若能确定满足情况的最大右区间位置,则以L为左区间的 、 满足题意的区间个数为 R-L+1 个。
所以解题思路为:
枚举每一个左区间, 二分来判定右区间的位置。累加即可。
二分的写法是:
对于当前的二分区间 [L,R] ,中点为m ,若 [L,M] 的最大值与最小值之差 大于等于k,则区间向左收敛;若满足题意,差值小于k,则向右(回退)收敛。
上代码:
#include "stdio.h"
#include "cstring"
#include "algorithm"
using namespace std;
#define inf 100009
#define INF 999999999
#define ll long long
#define loop(x,y,z) for(x=y;x<z;x++)
int n;
int mx[inf][20],mi[inf][20];
int a[inf];
void init_d()
{
memset(mx,0,sizeof mx);
memset(mi,0,sizeof mi);
int i,j;
loop(i,0,n)mx[i][0]=mi[i][0]=a[i];
for(j=1;(1<<j)<=n;j++)
for(i=0;i+(1<<j)<=n;i++)
{
mx[i][j]=max( mx[i][j-1],mx[i+(1<<(j-1))][j-1] );
mi[i][j]=min( mi[i][j-1],mi[i+(1<<(j-1))][j-1] );
}
}
int query(int i,int j)
{
int k=0;
while((1<<k)<=j-i+1)k++;
k--;
return max( mx[i][k],mx[j-(1<<k)+1][k] )-min( mi[i][k],mi[j-(1<<k)+1][k] );
}
int main()
{
int i,j,T,k;
ll ans;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
loop(i,0,n)scanf("%d",&a[i]);
init_d();
ans=0;
loop(i,0,n)
{
int l=i,r=n-1,m; //二分内容
while(l<=r)
{
m=l+(r-l)/2;
if(query(i,m)<k)l=m+1;
else r=m-1;
}
ans+=r-i+1;
}
printf("%lld\n",ans);
}
return 0;
}