[SCOI2014]方伯伯的玉米田

题目链接

算法:

         为了保证DP的正确与方便,这里先提供一个结论:每次操作都一定要拔高第n棵玉米。

         以下引用NS·YJD大佬的一篇博客的证明:

         首先无论操作区间在哪里,如果区间两边存在玉米,那么这些玉米与区间内玉米拔高后的相对高度关系只有3种情况:1.原本区间内比它们高的玉米还是比它们高。2.原本区间内比它们矮的玉米不再比它们矮。3.原本区间内比它们矮的玉米还是比它们矮。

         对于区间左边而言:

                这三种情况都不会减少初始排列中已存在的最长不下降序列的长度,并且有可能使它增长

         对于区间右边而言:

                这三种情况都不会增加初始排列中已存在的最长不下降序列的长度,并且有可能使它减少

         如此一来我们就可以发现,要想得到最长不下降序列,区间的右边不存在玉米的情况一定是最优解。

 

         由此,我们可以设f[i][j]为以第i个玉米高度为结尾,且第i个玉米拔高j次的最长上升子序列的长度。

         所以状态转移方程即为f[i][j]=max\left \{ f[x][y]\left ( a[x]+y<=a[i]+j,y<=j \right ) \right \},(此时“,"表示“且”)

         转移我们则可以用二维树状数组维护前缀最大值(相当于我们将f[i][j]的值转化为f[a[i]+j][j]的值,然后求f[1][1],f[1][2],....,f[1][j],...,f[a[i]+j][j]中的最大值)。

          注意:由于j的值可以为0,在作为树状数组的下标中,我们修改与查询就可以把第二位坐标+1。

          所求答案一目了然吧。。。。。

 

Code:

#include
#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 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 void qwq(T x){
	if(x>9)qwq(x/10);
	putchar(x%10+'0');
}
template void write(T x){
	if(x<0){x=-x;putchar('-');}
	qwq(x);putchar('\n');
}
template 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;
}

          

         

你可能感兴趣的:(树状数组,DP)