BZOJ3594&&洛谷P3287 [SCOI2014]方伯伯的玉米田

二维树状数组优化DP

手动再见

大毒瘤

这样的出题人就是丧病

n 2 n^2 n2只有10分!!!

讲一下思路

不难发现,每次拔高,必定是从序列中某个地方直接拔高到序列末尾,为啥?
我们假设没有拔到末尾,那么有以下几种情况
1.拔高区间有一些比左侧低的,现在不低于左侧了
2.拔高区间有一些比左侧高的,现在还是高于左侧
3.拔高区间有一些不高于右侧的,现在高于右侧了
也就是说拔高某一段,会造成
1.在它左侧从左向右的最长上升子序列变长
2.在它右侧从左向右的最长上升子序列变短
所以最优方案应该是直接拔到末尾,这样就只会使得ans变大或者不变
所以一个点被拔高的次数一定大于等于它左侧的点
然后我们考虑转移方程
对于一个点 i i i,假如他已经被拔高了 k k k次,考虑一个包含它的LIS,那么在它左侧的某个点 p p p假设被拔高了 j j j次,那么 0 < = j < = k 0<=j<=k 0<=j<=k,并且一定存在 h [ i ] + k > = h [ p ] + j h[i]+k>=h[p]+j h[i]+k>=h[p]+j
于是我们 就可以列出一个转移方程 f [ i ] [ j ] f[i][j] f[i][j]表示第 i i i个点被拔高了 j j j次的LIS长度,转移就是 f [ i ] [ j ] = max ⁡ ( f [ k ] [ l ] ) + 1 , ( 0 < k < i , 0 < = l < = j ) f[i][j]=\max(f[k][l])+1,(0<k<i,0<=l<=j) f[i][j]=max(f[k][l])+1(0<k<i0<=l<=j),发现需要一个二维的最大值,类比一维树状数组优化LIS,那么我们也用二维树状数组来优化一下这个假的二维LIS,我们 重新定义 f [ i ] [ j ] f[i][j] f[i][j],表示以高度 i i i结尾,且当前点被拔高了 j j j次的LIS,然后暴力转移就好了

注意更新次数可能为0,但树状数组没有0,所以我们给第二维加上一个1的偏移量

代码

//By AcerMo
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
using namespace std;
const int M=100500;
int n,m,e;
int h[M],s[6005][505];
inline void read(int &x)
{
	x=0;char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return ;
}
inline void add(int l,int v,int p)
{
	for (int i=l;i<=e+m;i+=lowbit(i))
	for (int k=p;k<=m+1;k+=lowbit(k))
	s[i][k]=max(s[i][k],v);
	return ;
}
inline int find(int l,int p)
{
	int ans=0;
	for (int i=l;i;i-=lowbit(i))
	for (int k=p;k;k-=lowbit(k))
	ans=max(ans,s[i][k]);
	return ans;
}
signed main()
{
	read(n);read(m);int ans=0;
	for (int i=1;i<=n;i++) 
	read(h[i]),e=max(h[i],e);
	for (int i=1;i<=n;i++)
	for (int k=m;k>=0;k--)
	{
		int x=find(h[i]+k,k+1)+1;
		ans=max(ans,x);
		add(h[i]+k,x,k+1);
	}
	cout<<ans;
	return 0;
}

你可能感兴趣的:(数据结构-树状数组,动态规划)