树状数组优化 LIS
原始题目
给定长度为 n n n 的序列 a 1 , a 2 , ⋯ , a n a_1, a_2, \cdots, a_n a1,a2,⋯,an,求该序列的 最长上升子序列 长度。
其中 1 ≤ n ≤ 1 0 3 1 \le n \le 10^3 1≤n≤103。
设以 a i a_i ai 结尾的最长上升子序列长度为 f ( i ) f(i) f(i),显然:
f ( i ) = max 1 ≤ j < i , a j < a i { f ( j ) } + 1 f(i) = \max_{1 \le j < i, a_j < a_i} \{f(j)\} + 1 f(i)=1≤j<i,aj<aimax{f(j)}+1
在本题中,暴力计算即可。时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
加强版
多组数据, 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1≤n≤105。
上面这条式子,相当于查找之前满足
a
j
<
a
i
a_j < a_i
aj<ai 的最大
f
f
f 值,显然可以将
a
j
a_j
aj 作为树状数组的下标,用树状数组进行动态维护。注意到题目中说
a
i
a_i
ai 是 a number fits a long integer
,需要离散化。若离散化后新的值为 id
,则通过 change(id, query(id - 1) + 1)
更新 id
的位置即可。
树状数组优化没多少思维含量的,相当直观。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 100005
using namespace std;
int n, m, a[MAXN], b[MAXN], f[MAXN];
void change(int x, int val) {
for (; x <= m; x += x & -x) {
f[x] = max(f[x], val);
}
}
int query(int x) {
int ans = 0;
for (; x; x -= x & -x) {
ans = max(ans, f[x]);
}
return ans;
}
int main() {
while (scanf("%d", &n) != EOF) {
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
b[i] = a[i];
}
sort(b + 1, b + n + 1);
m = unique(b + 1, b + n + 1) - b - 1;
memset(f, 0, sizeof(f));
for (int i = 1; i <= n; ++i) {
int id = lower_bound(b + 1, b + m + 1, a[i]) - b;
change(id, query(id - 1) + 1);
}
printf("%d\n", query(m));
}
return 0;
}
最后找全局最大值,用 query(m)
,直接 f[m]
肯定要 WA 掉。这个调了我一中午才发现写错了。
修改版
最大上升子序列和
没找到 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1≤n≤105 的,只能找到 1 ≤ n ≤ 1 0 3 1 \le n \le 10^3 1≤n≤103。
把原来的 change(id, query(id - 1) + 1)
改成 change(id, query(id - 1) + a[i])
就符合题意,其他代码不变。不写多测也能 AC?
整理了一遍,现在感觉树状数组优化 LIS 确实挺水的。树状数组非常好写,离散化也还好。