【传送门:51nod-1275】
简要题意:
给出一个有n个数的序列,求出有多少个子段满足子段内最大值与最小值的差小于等于k
题解:
单调队列做
j表示往右最长能覆盖到的那个数,用两个单调队列来维护j的范围,一个递增,一个递减
然后对于每个i,j的取值都可能不同,但是随着i增大,j不可能减小,所以每次维护完j,答案增加以i为左端点的贡献
这样做是O(n)的,因为j最大为n
然后每次i结束前,将队列中位置<=i的删掉,因为对于i+1及后面的数而言,i不能对它们造成影响
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; typedef long long LL; int a[51000]; int q1[51000],q2[51000]; int main() { int n,k; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int l1=1,l2=1,r1=0,r2=0; LL ans=0;int j=1; for(int i=1;i<=n;i++) { while(j<=n) { while(l1<=r1&&a[q1[r1]]<=a[j]) r1--; q1[++r1]=j; while(l2<=r2&&a[q2[r2]]>=a[j]) r2--; q2[++r2]=j; if(a[q1[l1]]-a[q2[l2]]>k) break; j++; } ans+=j-i; while(l1<=r1&&q1[l1]<=i) l1++; while(l2<=r2&&q2[l2]<=i) l2++; } printf("%d ",ans); return 0; }