链接: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;
}