干物妹小埋-树状数组-吉首大学2019年程序设计竞赛

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值