最大上升子序列和
给定一个长度为 n 的整数序列 a1,a2,…,an。
请你选出一个该序列的严格上升子序列,要求所选子序列的各元素之和尽可能大。
请问这个最大值是多少?
输入格式
第一行包含整数 n。
第二行包含 n 个整数 a1,a2,…,an。
输出格式
输出最大的上升子序列和。
数据范围
对于前三个测试点,1≤n≤4。
对于全部测试点,1≤n≤105,1≤ai≤109。
输入样例1:
2
100 40
输出样例1:
100
输入样例2:
4
1 9 7 10
输出样例2:
20
样例解释
对于样例 1,我们只选取 100。
对于样例 2,我们选取 1,9,10。
题解
最长上升子序列的一样的思路。
状态表示
f[i]表示 以a[i]为结尾的最大上升子序列的和。
状态划分
以倒数第二个数字划分
则可以为没有,a[0],a[1],…,a[i-1]
f[i] = max(f[j] + a[i], f[i]) 0<=j < i, a[j] < a[i]
但n = 1e5,O(n2)O(n^2)O(n2)复杂度会超时。采用树状数组或者线段树优化,维护f[i]的最大值,快速求f[0~i-1]的最大值。
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
typedef long long LL;
vector<int>xs;
int n, a[N];
LL f[N], tr[N];
int get(int x){
return lower_bound(xs.begin(), xs.end(), x) - xs.begin() + 1;
}
void add(int x, LL val){
while(x <= n){
tr[x] =max(val, tr[x]);
x += x&(-x);
}
}
LL query(int x){
LL res = 0;
while(x){
res = max(tr[x], res);
x -= x&(-x);
}
return res;
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
xs.push_back(a[i]);
}
sort(xs.begin(), xs.end());
xs.erase(unique(xs.begin(), xs.end()), xs.end());
LL ans = 0;
for(int i = 1; i <= n; i++){
int pos = get(a[i]);
f[i] = a[i] + query(pos - 1);
ans = max(f[i], ans);
add(pos, f[i]);
}
printf("%lld", ans);
return 0;
}