【YBTOJ】【Luogu P3287】[SCOI2014]方伯伯的玉米田
链接:
题目大意:
可以 \(k\) 次区间加一数列 \(a\),求最长不下降子序列长度。
正文:
最重要的一点是,区间加一操作的右端点一定是 \(n\)。假设原本想要区间 \([l,r]\) 加一,由于是求最长不下降子序列长度,所以 \([l,r]\) 加一后,肯定也小于等于 \([r+1,n]\) 的任意一点,所以直接 \([l,n]\) 对答案没有影响。
接着就是设 \(f_{i,j}\) 表示 \(a_i\) 被加过 \(j\) 次的最长不下降子序列长度,则有:
\[f_{i,j}=\max_{k<i,l\leq j,a_k+l<a_i+j}\{f_{k,l}+1\}
\]
可以用二维的树状数组维护。
代码:
int n, mx, m;
int t[M][N], ans;
int a[N];
void update(int x, int k, int val)
{
for (; x <= mx; x += x & -x)
for (int i = k; i <= m + 1; i += i & -i)
t[i][x] = max(t[i][x], val);
}
int query(int x, int k)
{
int ans = 0;
for (; x; x -= x & -x)
for (int i = k; i; i -= i & -i)
ans = max(t[i][x], ans);
return ans;
}
int main()
{
scanf ("%d%d", &n, &m);
memset (t, 0, sizeof t);
for (int i = 1; i <= n; i++)
scanf ("%d", &a[i]), mx = max(mx, a[i]);
mx += m;
for (int i = 1; i <= n; i++)
for (int j = m; ~j; j--)
{
int tmp = query(a[i] + j, j + 1) + 1;
ans = max(ans, tmp);
update(a[i] + j, j + 1, tmp);
}
printf ("%d\n", ans);
return 0;
}