最长上升子序列和

这篇博客介绍了如何使用树状数组和线段树来优化动态规划算法,以解决时间复杂度为O(n*n)的问题。作者首先展示了如何用树状数组进行优化,然后通过线段树实现进一步的优化,并结合离散化操作,确保在大数据范围内避免超时错误。代码示例中详细解释了每个关键步骤,包括线段树的构建、更新和查询操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们发现如果用dp来做的话
时间复杂度是O(n*n)
考虑数据范围一定会TLE所以我们用树状数组来优化
第二个代码是用线段树来优化的
当然了,还要用到离散化操作
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,w[100010],res,f[100010];
int tr[1000010];
vector<int>xs;

int get(int x){
    return lower_bound(xs.begin(),xs.end(),x)-xs.begin()+1;
}

int lowbit(int x){
    return x&-x;
}

void add(int x,int v){
    for(int i=x;i<=n;i+=lowbit(i)){
        tr[i]=max(tr[i],v);
    }
}

int query(int x){
    int sum=0;
    for(int i=x;i;i-=lowbit(i)){
        sum=max(sum,tr[i]);
    }
    return sum;
}

signed main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>w[i];
        xs.push_back(w[i]);
    }
    sort(xs.begin(),xs.end());
    xs.erase(unique(xs.begin(),xs.end())  ,  xs.end());
    for(int i=1;i<=n;i++){
        int k=get(w[i]);
        f[i]=query(k-1)+w[i];
        res=max(f[i],res);
        add(k,f[i]);
    }
    cout<<res;
}

这第二篇题解使用线段树写的
直接用线段树去更新,而没有再开一个dp数组去维护

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100010;
vector<int>xs;
int f[N],w[N],n;
struct node{
    int l,r,maxx;
}tr[N*4];

int get(int x){
    return lower_bound(xs.begin(),xs.end(),x)-xs.begin()+1;//线段树数组下标要从1开始所以要加1
    //清测不加1就wa了
    //return lower_bound(xs.begin(),xs.end(),x)-xs.begin();
}

void pushup(int u){
    tr[u].maxx=max(tr[u<<1].maxx,tr[u<<1|1].maxx);
}

void build(int u,int l,int r){
    if(l==r)tr[u]={l,r,0};
    else{
        tr[u]={l,r,-1};
        int mid=l+r>>1;
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
        pushup(u);
    }
}

void modify(int u,int l,int r,int v){
    if(tr[u].l>=l&&tr[u].r<=r)tr[u].maxx=v;
    else{
        int mid=tr[u].l+tr[u].r>>1;
        if(mid>=l)modify(u<<1,l,r,v);
        if(mid<r)modify(u<<1|1,l,r,v);
        pushup(u);
    }
}

int query(int u,int l,int r){
    if(tr[u].l>=l&&tr[u].r<=r)return tr[u].maxx;
    else{
        int mid=tr[u].l+tr[u].r>>1;
        int res=0;
        if(mid>=l)res=query(u<<1,l,r);
        if(mid<r)res=max(res,query(u<<1|1,l,r) );
        pushup(u);
        return res;
    }
    
    
}

signed main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>w[i];
        xs.push_back(w[i]);
    }
    sort(xs.begin(),xs.end());
    xs.erase(unique(xs.begin(),xs.end() ),xs.end()  );
    build(1,1,n);
    for(int i=1;i<=n;i++){
        int k=get(w[i]);
        if(k==1){
            modify(1,k,k,w[i]);
            continue;
        }
        int vmax=query(1,1,k-1);
        modify(1,k,k,vmax+w[i]);
    }
    //其实单点修改也是可以的
    cout<<query(1,1,n);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值