CF487 B. Strip
题意:
一个n个数的数组,要求把他们划分为最少的连续段,满足:
1、每段长度至少为l
2、每段的最大值-最小值不超过s
dp[i]表示前i个数最少要划分为多少段
枚举j(j<=i-l),若[j+1,i]的最大值-最小值不超过s,那么dp[i]=min(dp[i],dp[j]+1)
枚举j是n^2的,可以二分出满足极值差要求的区间,然后用线段树优化
#include<bits/stdc++.h> using namespace std; #define N 100003 int mx[N<<2],mi[N<<2],dp[N<<2]; int qm,qi,w; void build(int k,int l,int r) { dp[k]=2e9; if(l==r) { if(!l) dp[k]=0; else { scanf("%d",&mx[k]); mi[k]=mx[k]; } return; } int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); mx[k]=max(mx[k<<1],mx[k<<1|1]); mi[k]=min(mi[k<<1],mi[k<<1|1]); dp[k]=min(dp[k<<1],dp[k<<1|1]); } void query(int k,int l,int r,int opl,int opr) { if(l>=opl && r<=opr) { qm=max(qm,mx[k]); qi=min(qi,mi[k]); return; } int mid=l+r>>1; if(opl<=mid) query(k<<1,l,mid,opl,opr); if(opr>mid) query(k<<1|1,mid+1,r,opl,opr); } void change(int k,int l,int r,int pos,int x) { if(l==r) { dp[k]=x; return; } int mid=l+r>>1; if(pos<=mid) change(k<<1,l,mid,pos,x); else change(k<<1|1,mid+1,r,pos,x); dp[k]=min(dp[k<<1],dp[k<<1|1]); } void query2(int k,int l,int r,int opl,int opr) { if(l>=opl && r<=opr) { w=min(w,dp[k]); return; } int mid=l+r>>1; if(opl<=mid) query2(k<<1,l,mid,opl,opr); if(opr>mid) query2(k<<1|1,mid+1,r,opl,opr); } int main() { int n,s,l; scanf("%d%d%d",&n,&s,&l); build(1,0,n); int L,R,mid,tmp; for(int i=l;i<=n;++i) { L=1; R=i-l+1; tmp=-1; while(L<=R) { mid=L+R>>1; qm=-2e9; qi=2e9; query(1,0,n,mid,i); if(qm-qi<=s) { tmp=mid; R=mid-1; } else L=mid+1; } if(tmp!=-1) { w=2e9; if(tmp-1<=i-l) query2(1,0,n,tmp-1,i-l); if(w!=2e9) change(1,0,n,i,w+1); } } w=2e9; query2(1,0,n,n,n); if(w==2e9) w=-1; printf("%d",w); }