最大上升子序列和+树状数组维护区间最大值

给定一个长度为 nn 的整数序列 a1,a2,,an

请你选出一个该序列的严格上升子序列,要求所选子序列的各元素之和尽可能大。

请问这个最大值是多少?

输入格式

第一行包含整数 n

第二行包含 n 个整数 a1,a2,,an

输出格式

输出最大的上升子序列和。

数据范围

对于前三个测试点,1n4
对于全部测试点,1n1e5,1ai1e9

输入样例1:

2
100 40

输出样例1:

100

输入样例2:

4
1 9 7 10

输出样例2:

20

样例解释

对于样例 11,我们只选取 100

对于样例 22,我们选取 1,9,10

 

首先呢我们先写一个暴力,然后在优化。

我们令 f[i] 指的是以i结尾的最长上升子序列和。

然后这个代码就是:

f[1]=a[1];
for(int i=2;i<=n;i++){
    for(int j=1;j<=i;j++){
        if(a[i]>a[j]){
           f[i]=max(f[i],f[j]+a[i]);
        }
    } 
}
int ans=0;
for(int i=1;i<=n;i++){
    ans=max(ans,f[i]);
}
cout<<ans<<endl;

这样写显然是T的,然后我们要想一个方法来优化他,显然我们需要求的是小于a[i]的的并且小于i的f值的最大值。这个显然线段树和树状数组是可以解决的,

重要的是我们需要离散化

然后这个代码就是:

 

#include<iostream>
#include<algorithm>
#include<set>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1e6+100;
ll a[maxn],id[maxn],f[maxn];
ll c[maxn]; 
int cnt=0;
ll lowbit(ll x){
    return x&-x;
}
void add(int x,ll k){
    for(int i=x;i<=cnt;i+=lowbit(i)){
        c[i]=max(k,c[i]);
    }
} 
ll ask(ll x){
    ll ans=0;
    for(int i=x;i>0;i-=lowbit(i)){
        ans=max(ans,c[i]);
    }
    return ans;
}
int get(int x){
    return lower_bound(id+1,id+cnt+1,x)-id;
}
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i]; 
        id[i]=a[i];
    }
    sort(id+1,id+n+1);
    cnt=unique(id+1,id+n+1)-id-1;
    for(int i=1;i<=n;i++){
        int p=get(a[i]);
        f[i]=ask(p-1)+a[i];
        add(p,f[i]); 
    }
    ll ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,f[i]);
    }
    cout<<ans<<endl;
    return 0;
}

 

posted @ 2021-06-12 21:48  lipu123  阅读(67)  评论(0)    收藏  举报