题目链接: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; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-05 13:52:41