【51nod】【单调队列】连续子段的差异
题目
解题思路
因为区间的最大差异不超过m
所以用单调队列维护区间最大值和最小值
如果差值超出规定差异,将位置较小的退队,左边界为ta+1,右边界为当前位置
用一个数组将所有区间存储下来,区间中的子段个数为n*(n+1)(n为区间长度),将所有累加起来,再减去重叠的部分
代码
#include<iostream>
#include<cstdio>
using namespace std;
struct lzf{
int l,r;
}b[50010];
long long ans;
int n,m,w=1,t,h=1,t2,h2=1,l;
int q[50010],q2[50010],a[50010];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
while (h<=t&&a[q[t]]<=a[i]) t--;
while (h2<=t2&&a[q2[t2]]>=a[i]) t2--;
q[++t]=i,q2[++t2]=i;
while (a[q[h]]-a[q2[h2]]>m) //查找符合要求的左边界
{
if (q[h]<q2[h2]) w=q[h]+1,h++;
else w=q2[h2]+1,h2++;
}
b[++l]=(lzf){w,i};
}
for (int i=1;i<=l;i++)
{
int x=b[i].l,y=b[i].r;
ans+=1ll*(y-x+1)*(y-x+2)/2;
if (b[i-1].r>=x) //减去重叠
ans-=1ll*(b[i-1].r-x+1)*(b[i-1].r-x+2)/2;
}
printf("%lld",ans);
return 0;
}