Link
Solution
状态 (dp_i) 表示把前 (i) 个数可以划分成的最小段数。那么对于转移,就可以枚举一个 (j) 表示将 ((j,i]) 划成一段,那么就有 (dp_{i}=min{dp_j}+1).
这道题的关键在于如何维护出 (j) 的取值范围。首先 (j) 肯定必须小于等于 (i-l) ,因为每段长度不小于 (l)。其次,容易发现当 (i) 是固定的时候,(j) 的取值越小那么当前段的最值之差就越大(至少是不降的)。所以可以用单调队列维护出最值之差恰好不大于 (s) 的位置——这个过程可以用两个单调队列分别维护一下最大值和最小值。
那么现在我们就已经能知道决策区间的左右端点了,又发现这个区间的移动是单调的,即左右端点不减。那么这个 (dp) 的最小值也可以用单调队列维护。
复杂度 (O(n))
#include<stdio.h>
#define N 100007
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
/* q1 max q2 min */
int n,l,s,dp[N];
int pos=1,a[N],q1[N],q2[N],q3[N];
int h1=1,h2=1,h3=1,t1=0,t2=0,t3=0;
int main(){
n=read(),s=read(),l=read();
for(int i=1;i<=n;i++){
a[i]=read();
while(h1<=t1&&a[q1[t1]]<=a[i]) t1--;
while(h2<=t2&&a[q2[t2]]>=a[i]) t2--;
q1[++t1]=q2[++t2]=i;
if(i-l>=0){
while(h3<=t3&&dp[i-l]<=dp[q3[t3]]) t3--;
q3[++t3]=i-l;
}
while(pos<=i-l+1&&a[q1[h1]]-a[q2[h2]]>s){
if(h1<=t1&&q1[h1]<(++pos)) h1++;
if(h2<=t2&&q2[h2]<pos) h2++;
if(h3<=t3&&q3[h3]<pos-1) h3++;
}
if(h3<=t3){
if(dp[q3[h3]]==n+1) dp[i]=n+1;
else dp[i]=dp[q3[h3]]+1;
}else dp[i]=n+1;
}
printf("%d",(dp[n]!=n+1)? dp[n]:-1);
}