算法:
为了保证DP的正确与方便,这里先提供一个结论:每次操作都一定要拔高第n棵玉米。
以下引用NS·YJD大佬的一篇博客的证明:
首先无论操作区间在哪里,如果区间两边存在玉米,那么这些玉米与区间内玉米拔高后的相对高度关系只有3种情况:1.原本区间内比它们高的玉米还是比它们高。2.原本区间内比它们矮的玉米不再比它们矮。3.原本区间内比它们矮的玉米还是比它们矮。
对于区间左边而言:
这三种情况都不会减少初始排列中已存在的最长不下降序列的长度,并且有可能使它增长
对于区间右边而言:
这三种情况都不会增加初始排列中已存在的最长不下降序列的长度,并且有可能使它减少
如此一来我们就可以发现,要想得到最长不下降序列,区间的右边不存在玉米的情况一定是最优解。
由此,我们可以设为以第i个玉米高度为结尾,且第i个玉米拔高j次的最长上升子序列的长度。
所以状态转移方程即为,(此时“,"表示“且”)
转移我们则可以用二维树状数组维护前缀最大值(相当于我们将的值转化为
的值,然后求
中的最大值)。
注意:由于j的值可以为0,在作为树状数组的下标中,我们修改与查询就可以把第二位坐标+1。
所求答案一目了然吧。。。。。
Code:
#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define rep2(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
template<typename T> void read(T &num){
char c=getchar();num=0;T f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){num=(num<<3)+(num<<1)+(c^48);c=getchar();}
num*=f;
}
template<typename T> void qwq(T x){
if(x>9)qwq(x/10);
putchar(x%10+'0');
}
template<typename T> void write(T x){
if(x<0){x=-x;putchar('-');}
qwq(x);putchar('\n');
}
template<typename T> void chkmax(T &x,T y){x=x>y?x:y;}
int n,k,sx;
int co[10010];int tree[6010][510];
inline int lowbit(int x){return x&(-x);}
inline void change(int x,int y,int w){
while(x<=sx+k){
int pos=y;
while(pos<=k+1){chkmax(tree[x][pos],w);pos+=lowbit(pos);}
x+=lowbit(x);
}
return;
}
inline int query(int x,int y){
int ans=0;
while(x){
int pos=y;
while(pos){chkmax(ans,tree[x][pos]);pos-=lowbit(pos);}
x-=lowbit(x);
}
return ans;
}
int main(){
read(n);read(k);sx=0;
rep(i,1,n){read(co[i]);chkmax(sx,co[i]);}
rep(i,1,n){
rep2(j,k,0){change(co[i]+j,j+1,query(co[i]+j,j+1)+1);}
}
write(query(sx+k,k+1));
return 0;
}