题意
给你 (n) 个数,(k) 次操作,每次可以选则一段区间全部加一,最后问最长上升子序列最长可以是多长。
Analysis
考虑每次加的区间,若是中间的一段,则把后面一段一起加一肯定不会更劣,所以每次修改必然是一个后缀。
然后可以得到一个动态规划的算法:设子状态为 (dp_{i,j}) 代表到第 (i) 个位置,总共操作了 (j) 次。
有 (dp_{i,j}=max(dp_{p,q}+1)) 其中 (a_p+qle a_i+j,jle q)。
一个二维的最长上升子序列问题,我们可以用二维树状数组优化到 (O(nklog{v}log{k}))。
注意树状数组从 (1) 开始而我们的 (j) 是可以为 (0) 的,所以每次要加一,另外为了保证第一维(这其实是个三维偏序),我们第二层循环(j)要从大到小。
#include <bits/stdc++.h>
using namespace std;
int n, k;
int a[10010];
int dp[10010][510], f[10010][510];
int ans;
void cmax(int &x, int y) {
x = max(x, y);
}
int lowbit(int x) {
return x & (-x);
}
void change(int x, int y, int cur) {
for (int i = x; i <= 10000; i += lowbit(i)) {
for (int j = y; j <= k + 1; j += lowbit(j)) {
cmax(dp[i][j], cur);
}
}
}
int ask(int x, int y) {
int ret = 0;
for (int i = x; i; i -= lowbit(i)) {
for (int j = y; j; j -= lowbit(j)) {
cmax(ret, dp[i][j]);
}
}
return ret;
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
for (int j = k; j >= 0; j--) {
f[i][j] = ask(a[i] + j, j + 1) + 1;
change(a[i] + j, j + 1, f[i][j]);
ans = max(ans, f[i][j]);
}
}
printf("%d", ans);
return 0;
}