这道题我们很显然要用DP来做。
那么首先我们需要构造出一个DP方程
f[i]肯定由另一个状态dp转移后+1得到,那么这个状态是什么呢?
很明显就是mint[i]~i-k(在此mint表示的是以i为结尾的竹子最左端最多能延展到的位置)
看到这个之后我们想到了一个N^2DP
但是还是不够
我们想一想如何得到mint?
mint就是判断一段区间的最大值减去最小值是否满足题意
那么我们可不可以用数据结构得出这个答案呢?
显然我们可以用线段树来优化这个过程
时间复杂度O(nlogn)
同样在得出答案的过程我们也可以用线段树加速
下面贴代码
#include<cstdio> #include<cstring> #define inf 0x3f3f3f3f #define min(x,y) (x<y?x:y) #define max(x,y) (x>y?x:y) using namespace std; int a[100005]; struct mm{ int v1,v2; }t[300005]; int mint[100005]; int f[300005]; int m=1,n,s,l; void pushdown(int i){t[i].v1=min(t[i<<1].v1,t[i<<1|1].v1);t[i].v2=max(t[i<<1].v2,t[i<<1|1].v2);} void buildtree(){ while(m<n+2)m<<=1; for(int i=1;i<=n;i++) t[i+m].v1=t[i+m].v2=a[i]; for(int i=m-1;i;i--) pushdown(i); } int query(int l,int r) { int maxn=-inf,minn=inf; for(l=l+m-1,r=r+m+1;l^r^1;l>>=1,r>>=1) { if(~l&1)minn=min(minn,t[l^1].v1),maxn=max(maxn,t[l^1].v2); if(r&1)minn=min(minn,t[r^1].v1),maxn=max(maxn,t[r^1].v2); } return maxn-minn; } int que(int l,int r) { int minn=inf; for(l+=m-1,r+=m+1;l^r^1;l>>=1,r>>=1) { if(~l&1)minn=min(minn,f[l^1]); if(r&1)minn=min(minn,f[r^1]); } return minn; } void push(int x){f[x]=min(f[x<<1],f[x<<1|1]);} void add(int x,int y){for(f[x+=m]=y,x>>=1;x;x>>=1)push(x);} int main(){ memset(f,127/3,sizeof(f)); scanf("%d%d%d",&n,&s,&l);n++; for(int i=2;i<=n;i++)scanf("%d",&a[i]); a[1]=a[2]; buildtree(); int le=1; for(int i=1;i<=n;i++) { int tmp=query(le,i); while(tmp>s) { tmp=query(++le,i); } mint[i]=le; } add(1,0); for(int i=2;i<=n;i++) if(max(1,mint[i]-1)>i-l)add(i,inf); else add(i,que(max(1,mint[i]-1),i-l)+1); printf("%d\n",f[n+m]>=inf?-1:f[n+m]); return 0; }
时间: 2024-10-06 10:36:08