题目链接:5289 Assignment
题意:给出n和K,表示有一串n个数的序列,存在多少个区间,该区间中任意两个数的差小于k
思路:
1.区间任意两个数的小于K 等价于 区间max-min<k,用RMQ来维护,区间最大最小值
2.最后暴力枚举区间必定要超时,发现随着区间的扩大max-min的值也在变大(非递减),有单调性就容易想到二分,所以是枚举左端点,二分找右端点。
AC代码:
#include<stdio.h>
#include <algorithm>
using namespace std;
#define LL int
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const LL MAXN = 100010;
LL dp[3][MAXN][20];
LL mm[MAXN],a[MAXN];
//初始化RMQ, b数组下标从1开始,从0开始简单修改
void initRMQ(LL n,LL b[]) {
mm[0] = -1;
for(LL i = 1; i <= n; i++) {
mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1];
dp[0][i][0] = dp[1][i][0]=b[i];
}
for(LL j = 1; j <= mm[n]; j++) {
for(LL i = 1; i + (1<<j) -1 <= n; i++) {
dp[0][i][j] = max(dp[0][i][j-1],dp[0][i+(1<<(j-1))][j-1]);
dp[1][i][j] = min(dp[1][i][j-1],dp[1][i+(1<<(j-1))][j-1]);
}
}
}
//查询最大值
LL rmq(LL x,LL y) {
LL k = mm[y-x+1];
LL tmp=max(dp[0][x][k],dp[0][y-(1<<k)+1][k]);
tmp-=min(dp[1][x][k],dp[1][y-(1<<k)+1][k]);
return tmp;
}
int main() {
LL t,i,n;
LL k;
scanf("%d",&t);
while(t--) {
scanf("%d %d",&n,&k);
for(i=1; i<=n; i++)
scanf("%d",&a[i]);
initRMQ(n,a);
LL l,r,m;
__int64 ans=0;
for(i=1; i<=n; i++) {
l=i,r=n;
LL t1,t2,tmp;
while(l<=r) {
m=(l+r)/2;
tmp=rmq(i,m);
if(tmp<k) l=m+1;
else r=m-1;
}
//printf(".....[l %d m%d r%d],tmp=%d\n",l,m,r,tmp);
ans+=(__int64)(l-i);
}
printf("%I64d\n",ans);
}
return 0;
}