题目链接:https://ac.nowcoder.com/acm/contest/992/B
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
在之前很火的一个动漫《干物妹小埋》中,大家对小埋打游戏喝可乐的印象十分的深刻。
现在欧尼酱将小埋的快乐水全部分开藏在了家具的顶端。
小埋使出空中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值总和。
不知道有多少人和我一样一开始打算把2e5个happy值都离散化然后拆开。比如说,对于第i个家具fur[i],本来只有这一个fur[i],我们现在把这一个i拷贝成happy[i]个fur[i]。那么把每个家具都这样拷贝完之后,答案不就成了最长上升(不严格上升)子序列的长度直接上upper_bound就行了吗?然而数据会炸掉,2e5个1e9复杂度直接上天。(即使把happy值离散化,空间复杂度还是太大,其次离散化之后单个大小顺序虽然没变,但是加起来就不对了。原谅我想着离散化就可以解决了)
所以说改变方向,既然已经想到了最长上升子序列长度,不是还有树状数组这一写法吗。见https://blog.csdn.net/lxt_Lucia/article/details/81206439 对最长上升子序列解法讲的挺好。
求“最长上升子序列长度”时,先给每个物品按照高度排序,每来一个物品i,我们是先找到pos[i](表示物品i最开始的位置)前面的最大值max[i],然后以i结尾的最长上升子序列长度就成了:max[i]+1。现在物品多了一个happy值,就是说有happy[i]个物品i。把max[i]+1替换成max[i]+happy[i]不就符合题意了吗?
上程序:
#include <cstdio>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#include <math.h>
#include <queue>
using namespace std;
typedef long long ll;
const int max_n=2e5+10;
ll A[max_n];
int n;
struct Fur
{
ll a,v;
int pos;
friend bool operator <(const Fur &a,const Fur &b)
{
return a.a==b.a?a.pos<b.pos:a.a<b.a;
}
}fur[max_n];
int get_k(int x)
{
return x&(-x);
}
void add(int i,ll x)
{
while(i<=n)
{
A[i]=max(A[i],x);
i=i+get_k(i);
}
}
ll query(int i)
{
ll res=0;
while(i>0)
{
res=max(res,A[i]);
i=i-get_k(i);
}
return res;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
int a,v;
memset(A,0,sizeof(A));
for(int i=1;i<=n;i++)
scanf("%lld",&fur[i].a);
for(int i=1;i<=n;i++)
scanf("%lld",&fur[i].v);
for(int i=1;i<=n;i++) fur[i].pos=i;
sort(fur+1,fur+1+n);
ll ret=0;
for(int i=1;i<=n;i++)
{
ll ma=query(fur[i].pos);
ma+=fur[i].v;
add(fur[i].pos,ma);
ret=max(ret,ma);
}
printf("%lld\n",ret);
}
}