洛谷 P3287

不难发现一定是拔高一段后缀。

所以设 \(f_{i,j}\) 表示考虑前 \(i\) 个位置,拔高 \(j\) 次,第 \(i\) 个位置强制选的 LIS 的长度。

则有 \(f_{i,j}=\max\limits_{1\le x\lt i,0\le y\le j,a_x+y\le a_i+j}\left\{f_{x,y}+1\right\}\)

\(x\lt i\) 天然满足,而剩下两个条件可以用二维 BIT 优化。

因为 \(j\) 可以为 \(0\) 所以要整体右移一位。

而且不难发现为了不让同一个 \(i\) 之间转移所以像背包一样 \(j\) 倒序枚举。

时间复杂度 \(\mathcal O(nk\log k\log w)\),只能说非常离谱。

Code:

#include <bits/stdc++.h>
using namespace std;
const int N = 10005, M = 5005, K = 505;

void chkmax(int &a, int b) { if (a < b) a = b; }

int n, m;
int a[N], f[N][K], c[K][K+M];

void upd(int x, int y, int v) {
	for (int i = x; i <= m + 1; i += i & -i)
		for (int j = y; j <= 5500; j += j & -j) chkmax(c[i][j], v);
}

int query(int x, int y) {
	int res = 0;
	for (int i = x; i; i -= i & -i)
		for (int j = y; j; j -= j & -j) chkmax(res, c[i][j]);
	return res;
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	for (int i = 1; i <= n; ++i)
		for (int j = m; ~j; --j)
			upd(j + 1, a[i] + j, f[i][j] = query(j + 1, a[i] + j) + 1);
	printf("%d", query(m + 1, 5500));
	return 0;
}
posted @ 2022-11-07 13:48  Kobe303  阅读(19)  评论(0编辑  收藏  举报