一个结论:每一次拔高玉米的区间的右端点一定是
。
正确性:如果凭空将
的玉米高度加
,那么最长不降子序列的长度一定会增加。所以当
时,操作区间
绝对比
优。
根据这个结论,可以设一个状态定义:
表示到了第
个玉米,拔了
次,以
为结尾的最长不降子序列长度。
转移:
也就是说,由于上面的结论得到,拔高后第 个玉米的高度为 ,第 个玉米的高度为 ,同时区间 被拔高的次数不能是负数。可以由上面两点限制得出转移条件。
还是不足以通过。由于转移条件的特殊性(是 的形式),所以可以使用二维树状数组优化。
具体地,第一维是 ,第二维是 (考虑到第二维 可能为 ,因此下标要加 ),每计算出一个 就将其存入树状数组。
这时候,为了避免 从 转移,枚举 的第二维要从 到 倒着枚举。
复杂度 。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Pos(x, k) for (; x <= k; x += x & -x)
#define Neg(x) for (; x; x -= x & -x)
using namespace std;
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 1e4 + 5, M = 555;
int n, K, a[N], f[N][M], T[N][M], ans;
void change(int x, int y, int v)
{
Pos(x, 7371)
{
int z = y;
Pos(z, 512) T[x][z] = max(T[x][z], v);
}
}
int ask(int x, int y)
{
int res = 0;
Neg(x)
{
int z = y;
Neg(z) res = max(res, T[x][z]);
}
return res;
}
int main()
{
int i, j;
n = read(); K = read();
For (i, 1, n) a[i] = read();
For (i, 1, n) Rof (j, K, 0)
{
ans = max(ans, f[i][j] = ask(a[i] + j, j + 1) + 1);
change(a[i] + j, j + 1, f[i][j]);
}
cout << ans << endl;
return 0;
}