洛谷 P3287 [SCOI2014] 方伯伯的玉米田 题解

发布时间 2023-10-27 20:43:56作者: jiangtaizhe001

题目传送门

题目大意

给定一个长度为 \(N\) 的序列 \(a\),可以进行最多 \(K\) 次操作,每次操作可以选择一个区间加 \(1\)
求操作之后最长的最长不降升子序列长度。
\(1\le N\le 10^4\)\(1\le K \le 500\)\(1\le a_i\le 5000\)

题目解析

要让最长不降升子序列最长,那么显然要让后面的数更大,所以每次操作的右端点选择 \(N\) 肯定不劣。
\(f(i,j)\) 为前 \(i\) 个 LIS 为 \(i\),使用了 \(k\) 次操作的最长 LIS 长度。
假设从 \(k\) 转移,如果 \(a_k\le a_i\),直接转移,否则使用操作把 \(a_i\) 变成和 \(a_k\) 一样大即可。

\[f(i,j)=\max\limits_{1\le k < i} \left\{ \begin{aligned} f(k,j)+1 & & a_k\le a_i\\ f(k,j+a_i-a_k)+1 & & a_k>a_i \end{aligned} \right. \]

朴素刷表转移为 \(O(N^2K)\)

考虑优化。
第一种转移,转移前后的 \(j\) 相同,每次从更小或者相同的 \(a_i\) 转移,对于每个 \(j\) 维护 \(low(j,V)=\max\limits_{a_i\le V} f(i,j)\)
第二种转移,转移前后的 \(a_i+j\) 相同,每次从更小或者相同的 \(j\) 转移,对于每个 \(a[i]+j\) 维护 \(over(A,B)=\max\limits_{a_i+j=A,j\le B}f(i,j)\)
开若干个树状数组维护前缀最大值即可。
时间复杂度 \(O(nk\log a_i)\)

#define maxn 10039
int n,m,k,a[maxn],f[maxn][539],ans;
class BIT{
public:
	#define lowbit(x) (x&-x)
	int c[5539];
	int query(int x){ int s=-1; while(x) s=mmax(s,c[x]),x-=lowbit(x); return s; }
	void add(int x,int y){ while(x<=m) c[x]=mmax(c[x],y),x+=lowbit(x); return; }
}lo[539],ov[5539],sum;

int main(){
	fin>>n>>k; int i,j; for(i=1;i<=n;i++) fin>>a[i],m=mmax(a[i],m);
	for(i=1;i<=n;i++) for(j=0;j<=k;j++){
		f[i][j]=mmax(lo[j].query(a[i]),ov[a[i]+j].query(j+1))+1;
		ans=mmax(ans,f[i][j]);
		lo[j].add(a[i],f[i][j]);
		ov[a[i]+j].add(j+1,f[i][j]);
	}
	fin<<ans<<'\n';
	return 0;
}