一个结论:每一次拔高玉米的区间的右端点一定是nn。
正确性:如果凭空将的玉米高度加11,那么最长不降子序列的长度一定会增加。所以当时,操作区间[l,n][l,n]绝对比[l,r][l,r]优。
根据这个结论,可以设一个状态定义:
f[i][j]f[i][j]表示到了第ii个玉米,拔了次,以ii为结尾的最长不降子序列长度。
转移:
也就是说,由于上面的结论得到,拔高后第ii个玉米的高度为,第hh个玉米的高度为,同时区间[i,n][i,n]被拔高的次数不能是负数。可以由上面两点限制得出转移条件。
还是不足以通过。由于转移条件的特殊性(是Ax≥Ay and Bx≥ByAx≥Ay and Bx≥By的形式),所以可以使用二维树状数组优化。
具体地,第一维是ai+jai+j,第二维是jj(考虑到第二维可能为00,因此下标要加),每计算出一个f[i][j]f[i][j]就将其存入树状数组。
这时候,为了避免f[i][j]f[i][j]从f[i][<j]f[i][<j]转移,枚举ff的第二维要从到00倒着枚举。
复杂度。
代码:
#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;
}