传送门
首先要发现,每一次选择拔高的区间都必须包含最右边的端点
为什么呢?因为如果拔高了一段区间,那么这段区间对于它的左边是更优的,对它的右边会更劣,所以我们每一次选的区间都得包含最右边的端点
我们枚举$i$表示考虑到第$i$个玉米,设$dp[j][k]$表示为$j$,$i$被覆盖次数为$k$时的最大长度,那么不难发现$j=h[i]+k$
那么很明显转移是$dp[j][k]=max\{dp[a][b]\}(a\leq j,b\leq k)$(因为它左边的覆盖次数不可能大于它,而且得满足是一个单调不降序列)
于是用二维树状数组维护即可
然后因为树状数组不能取到0,所以把树状数组的第二位整体右移一位
1 //minamoto 2 #include3 #include 4 using namespace std; 5 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 6 char buf[1<<21],*p1=buf,*p2=buf; 7 template<class T>inline bool cmax(T&a,const T&b){return a1:0;} 8 int read(){ 9 #define num ch-'0' 10 char ch;bool flag=0;int res; 11 while(!isdigit(ch=getc())) 12 (ch=='-')&&(flag=true); 13 for(res=num;isdigit(ch=getc());res=res*10+num); 14 (flag)&&(res=-res); 15 #undef num 16 return res; 17 } 18 const int N=1e4+5,K=505,M=6005; 19 int n,k,mx,ans; 20 int h[N],c[M][K]; 21 void update(int x,int y,int z){ 22 for(;x<=mx+k;x+=x&-x) 23 for(int i=y;i<=k+1;i+=i&-i) 24 cmax(c[x][i],z); 25 } 26 int query(int x,int y){ 27 int res=0; 28 for(;x;x-=x&-x) 29 for(int i=y;i;i-=i&-i) 30 cmax(res,c[x][i]); 31 return res; 32 } 33 int main(){ 34 // freopen("testdata.in","r",stdin); 35 n=read(),k=read(); 36 for(int i=1;i<=n;++i) cmax(mx,h[i]=read()); 37 for(int i=1;i<=n;++i) for(int j=k;j>=0;--j){ 38 int x=query(h[i]+j,j+1)+1;cmax(ans,x); 39 update(h[i]+j,j+1,x); 40 } 41 printf("%d\n",ans); 42 return 0; 43 }