CF487B Strip
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);
}