吉首大学2019年程序设计竞赛-B题(线段树)

链接:https://ac.nowcoder.com/acm/contest/992/B
来源:牛客网

题目描述

在之前很火的一个动漫《干物妹小埋》中,大家对小埋打游戏喝可乐的印象十分的深刻。
现在欧尼酱将小埋的快乐水全部分开藏在了家具的顶端。
小埋使出空中1080°转身接战术翻滚跳到任一家具上,她相信,只要她翻滚的足够快,欧尼酱就跟不上她。

1.为获取梦幻开局,小埋一套技能可以使她一开始掉落在任一家具上。
2.小埋家的家具按顺序给出,每个家具可跳可不跳,为避开欧尼酱的追击,小埋翻滚到某个家具上面后,只能向前继续翻滚。
3.启动超重力感应系统的小埋不会从较高的家具翻滚到较低的家具上。
4.由于每个家具上的快乐水都有对应的happy值,IQ==250的小埋会选择一条happy值总和最大的路线。

那么,最终小埋将获得的happy值总和是多少呢?

输入描述:

第一行一个整数n(0<n<=200000),表示小埋家的家具数。

第二行n个整数,对于每个整数ai, 0<=ai<=10^9,表示第i个家具的高度。

第三行n个整数,对于每个整数vi, 0<=vi<=10^9,表示第i个家具上的快乐水的happy值。

输出描述:

一个整数,表示小埋获得的happy值总和。

示例1
输入

6
2 1 1 3 3 4
3 1 1 1 1 1

输出

6

说明

路线:2->3->3->4
答案:3+1+1+1



分析:

dp[i]:以 i 结尾的序列的最大的 v 之和

那么很容易得到状态转移方程:dp[i] = max{ dp[j] } + v[i] ( j<i && a[j] <a[i] )
即找到前面最大的可以接上的序列。

关键是怎么获得在条件 j<i && a[j] <a[i] 下的max{ dp[j] } ,直接暴力查询的话总的时间复杂度会达到O(N2)

这里就需要用到线段树来维护了,以 a[i] 作为下标,dp作为值,线段树中存储序列结尾高度为a[i]的最大的dp值,每次只需要查询区间 [1, a[i] ] 中现有的max值,以此保证a[j] <a[i],又因为线段树初始为0,max值是逐步更新进去的,所以保证了找到的max值一定满足 j<i

因为a[i]比较大,所以要先离散化处理(并不会改变相对大小)



以下代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#define PII pair<int,int>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int MOD=1e9+7;
const int maxn=2e5+50;
int n,a[maxn];
LL v[maxn],t[maxn<<2];
void init()
{
    int b[maxn];
    for(int i=1;i<=n;i++)
        b[i]=a[i];
    sort(b+1,b+n+1);   //排序
    int m=unique(b+1,b+n+1)-(b+1);   //去重
    for(int i=1;i<=n;i++)
        a[i]=lower_bound(b+1,b+m+1,a[i])-b;
}
void updata(int rt,int l,int r,int x,LL num)   //单点更新
{
    if(l==r)
    {
        t[rt]=max(num,t[rt]);     //维护最大值
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)
        updata(rt<<1,l,mid,x,num);
    else
        updata(rt<<1|1,mid+1,r,x,num);
    t[rt]=max(t[rt<<1],t[rt<<1|1]);
}
LL query(int rt,int l,int r,int ql,int qr)     //区间查询
{
    if(ql<=l&&r<=qr)
        return t[rt];
    if(r<=ql||qr<l)
        return 0;
    int mid=(l+r)>>1;
    return max(query(rt<<1,l,mid,ql,qr),query(rt<<1|1,mid+1,r,ql,qr));
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        scanf("%lld",&v[i]);
    init();     //离散化处理
    memset(t,0,sizeof(t));   //线段树初始化为0
    LL ans=0,dp[maxn];
    for(int i=1;i<=n;i++)
    {
        dp[i]=query(1,1,n,1,a[i])+v[i];   //查询高度 1~a[i]中现有的最大dp[i]
        updata(1,1,n,a[i],dp[i]);   //更新高度为a[i]的最大值
        ans=max(ans,dp[i]);
    }
    printf("%lld\n",ans);
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值