3662. 最大上升子序列和
题目链接
3662. 最大上升子序列和
给定一个长度为 \(n\) 的整数序列 \(a_1,a_2,…,a_n\)。
请你选出一个该序列的严格上升子序列,要求所选子序列的各元素之和尽可能大。
请问这个最大值是多少?
输入格式
第一行包含整数 \(n\)。
第二行包含 \(n\) 个整数 \(a_1,a_2,…,a_n\)。
输出格式
输出最大的上升子序列和。
数据范围
对于前三个测试点,\(1≤n≤4\)。
对于全部测试点,\(1≤n≤10^5,1≤a_i≤10^9\)。
输入样例1:
2
100 40
输出样例1:
100
输入样例2:
4
1 9 7 10
输出样例2:
20
样例解释
对于样例 \(1\),我们只选取 \(100\)。
对于样例 \(2\),我们选取 \(1,9,10\)。
解题思路
树状数组,dp
-
状态表示:\(f[i]\) 为以 \(i\) 结尾的最大子序列和
-
状态计算:\(f[i]=max(f[i],f[j]+a[i])\),其中 \(a[i]>a[j]\)
考虑优化,其中关键在于找出满足 \(a[i]>a[j]\) 的 \(f[j]\) 的最大值,可以用树状数组处理,这里树状数组用来处理当前小于 \(a[i]\) 的最大的 \(f[j]\),同时由于值过大可离散化
- 时间复杂度:\(O(nlogn)\)
代码
// Problem: 最大上升子序列和
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/3665/
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e5+5;
int n,a[N];
LL f[N],tr[N];
vector<int> v;
int find(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void add(int x,LL y)
{
for(;x<=v.size();x+=x&-x)tr[x]=max(tr[x],y);
}
LL ask(int x)
{
LL res=0;
for(;x;x-=x&-x)res=max(res,tr[x]);
return res;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
v.pb(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for(int i=1;i<=n;i++)
{
f[i]=max(f[i],1ll*a[i]+ask(find(a[i])-1));
add(find(a[i]),f[i]);
}
cout<<*max_element(f+1,f+1+n);
return 0;
}