0%

3662.最大上升子序列和

3662.最大上升子序列和

题目描述:

给定一个长度为 $ n $ 的整数序列 $ a_1, a_2,\cdots,a_n $ 。选出一个该序列的严格上升子序列,并且子序列各元素的和尽可能大。请问最大值是多少?

数据范围:
$ 1\le n \le 10^5,1\le a_i\le 10^9 $

题解:

很容易想到使用动态规划,容易想到线性dp方程

但是需要双层循环,而 $ n $ 比较大,时间爆炸。因此需要优化,容易想到使用树状数组或线段树。这里使用树状数组。使用树状数组需要维护 $ [1, a[i] - 1] $ 的最大值。但是 $ a[i] $ 太大了,需要离散化。

离散化

离散化将原数组映射到一个数值比较小的连续的新数组。一般有两种方式。

第一种方法,使用 $ unordered\_map $ :

1
2
3
4
5
int size = 0;
for(int i = 0; i < n; ++i)
{
if(!mp.count(a[i])) mp[a[i]] = size++;
}

可以 $ size++ $ 得到 $ [0,size-1] $ 的新数组,也可以 $ ++size $ ,得到 $ [1,size] $ 的新数组。注意如果要保持大小关系,可以提前排序。

对于下标 $ i $ ,原数组值 $ a[i] $ ,新数组值 $ mp[a[i]] $ 。

第二种方法,使用 $ sort,unique,lower\_bound $ :

1
2
3
4
5
6
7
b = a;
sort(b, b+n);
int size = unique(b, b + n) - b;
for(int i = 0; i < n; ++i)
{
a[i] = lower_bound(b, b + size, a[i]) - b;
}

这样得到的新数组的值是 $ [0,size - 1] $ ,也可以先在 $ b[n] $ 存入一个非常小的值,然后排序。得到 $ [1,size] $

通过二分查找得到了新的数组,对于下标 $ i $ ,原数组值 $ b[a[i]] $ ,新数组值 $ a[i] $

总的来说,还是 $ map $ 比较简单,但是速度会慢一点。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
using namespace std;
using namespace FAST_IO;
const ll mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INF_LL = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-5;
const int maxn = 1e5 + 10;
const int maxm = 1e5 + 10;
int t, n, m, k;
int a[maxn];
ll dp[maxn]; // dp[i]表示以i结尾的最大的
// dp[i] = max(dp[j] + sum, dp[i]);
ll max_size, c[maxn];
int lowbit(int x)
{
return x & -x;
}

void insert(int index, ll val)
{
while (index <= max_size)
{
c[index] = max(c[index], val);
index += lowbit(index); // 跳父亲
}
}

ll query(int index)
{
ll ans = 0;
while (index >= 1)
{
ans = max(ans, c[index]);
index -= lowbit(index);
}
return ans;
}
int b[maxn];
int main()
{
// #define COMP_DATA
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
read(n);
for (int i = 0; i < n; ++i)
{
read(a[i]);
b[i] = a[i];
}
// b 作为临时数组
b[n] = -1e9 - 1;
sort(b, b + n + 1);
int size = unique(b, b + n + 1) - b;
// 对 a 离散化之后的a
for (int i = 0; i < n; ++i)
{
a[i] = lower_bound(b, b + size, a[i]) - b; // 注意加1,因为树状数组是 [1,max_size]
}
max_size = size;

ll ans = 0;
for (int i = 0; i < n; ++i)
{
dp[i] = query(a[i] - 1) + b[a[i]];
insert(a[i], dp[i]);
ans = max(ans, dp[i]);
}
cout << ans << endl;
return 0;
}