题目链接:Codeforces - Strip
显然可以dp。
然后枚举右端点的时候,左端点具有二分性,然后在对于的区间当中从最小的值转移即可。
最小值可以用线段树维护。
不过似乎这个是单调的,可以直接从最左端的点转移。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e5+10;
int n,cl,s,f[N][20],g[N][20],mi[N<<2],dp[N],a[N];
int ask(int l,int r,int fg){int k=log2(r-l+1);return fg?max(f[l][k],f[r-(1<<k)+1][k]):min(g[l][k],g[r-(1<<k)+1][k]);
}
int check(int l,int r){return ask(l,r,1)-ask(l,r,0)<=s;}
#define mid (l+r>>1)
void change(int p,int l,int r,int x,int v){if(l==r){mi[p]=min(mi[p],v); return ;}if(x<=mid) change(p<<1,l,mid,x,v);else change(p<<1|1,mid+1,r,x,v);mi[p]=min(mi[p<<1],mi[p<<1|1]);
}
int ask(int p,int l,int r,int ql,int qr){if(l==ql&&r==qr) return mi[p];if(qr<=mid) return ask(p<<1,l,mid,ql,qr);else if(ql>mid) return ask(p<<1|1,mid+1,r,ql,qr);else return min(ask(p<<1,l,mid,ql,mid),ask(p<<1|1,mid+1,r,mid+1,qr));
}
#undef mid
signed main(){cin>>n>>s>>cl; memset(mi,0x3f,sizeof mi),memset(dp,0x3f,sizeof dp);memset(g,0x3f,sizeof g);for(int i=1;i<=n;i++) scanf("%d",&a[i]),f[i][0]=g[i][0]=a[i];for(int j=1;j<=18;j++) for(int i=1;i<=n-(1<<j)+1;i++)f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]),g[i][j]=min(g[i][j-1],g[i+(1<<(j-1))][j-1]);dp[0]=0; change(1,0,n,0,0);for(int i=cl;i<=n;i++){if(!check(i-cl+1,i)) continue;int L=1,R=i-cl+1;while(L<R){int mid=L+R>>1;if(check(mid,i)) R=mid;else L=mid+1;}dp[i]=ask(1,0,n,L-1,i-cl)+1;change(1,0,n,i,dp[i]);}if(dp[n]>=0x3f3f3f3f) puts("-1");else cout<<dp[n];return 0;
}