jzoj 6068.全连 dp+树状数组

Description
【题目背景】
还记得若干年前那段互相比较《克罗地亚狂想曲》的分数的日子吗?
【题目描述】
E.Space 喜欢打音游。
但是他技术不好,总是拿不到全连(Full Combo)。
现在他面前有一份乐谱,乐谱的其中一段有 n 个连续的单键音符。
相邻两个音符的到来时间均相等,我们可以认为第 i 个音符会在第 i 个时刻到来。
点击一个音符,E.Space 需要一段准备时间来进行移动手指之类的操作。由于音符
的位置和周围情况不同,点击每个音符的准备时间也不同。
在一个音符的准备时间内,E.Space 没法做到去点击其它音符,但是不同音符的准
备时间范围可以互相重叠。形式化地,令第 i 个音符的准备时间为 ti 个单位时间,那么
如果 E.Space 选择去点击第 i 个音符,那么他就没法点击所有到来时刻在 (i − ti
, i + ti)
中的音符。
为了获得更高的分数,E.Space 还计算了每个音符的性价比。一个音符的性价比等
于点击这个音符得到的分数除以 E.Space 点击它所需要的准备时间。
E.Space 就不指望全连了,他只是想让你帮他计算一下他最多可以得到多少分数。

Input
从文件 fc.in 中读入数据。
第一行一个正整数 n 。
第二行 n 个正整数,第 i 个正整数表示 ti 。
第三行 n 个正整数,第 i 个正整数表示第 i 个音符的性价比 ai。

Output
输出到文件 fc.out 中。
一行一个正整数,表示 E.Space 可能达到的最高分数

Sample Input
5
2 3 2 1 2
3 1 2 9 4

Sample Output
18

Data Constraint
在这里插入图片描述

Hint
E.Space 可以选择点击第 1, 3, 5 个音符,分数为 2 × 3 + 2 × 2 + 2 × 4 = 18

分析:
f [ i ] f[i] f[i]表示选了第 i i i个时刻的音符的答案。转移显然。
f [ i ] = m a x ( f [ j ] + a [ i ] ∗ t [ i ] ) f[i]=max(f[j]+a[i]*t[i]) f[i]=max(f[j]+a[i]t[i])
其中 j ≤ i − t i j≤i-t_i jiti j + t j ≤ i j+t_j≤i j+tji
显然第二个约束是单调的,我们按顺序枚举 i i i,把这个时刻 j + t j = i j+t_j=i j+tj=i f f f插入树状数组,然后查询 1 1 1 i − t i i-t_i iti的最大值即可。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#define LL long long

const int maxn=1e6+7;

using namespace std;

int n;
int b[maxn];
LL a[maxn],f[maxn],t[maxn];
LL ans;

vector <int> num[maxn];

void updata(int x,LL y)
{
	for (int i=x;i<=n;i+=i&(-i)) t[i]=max(t[i],y);
}

LL getmax(int x)
{
	LL ans=0;
	for (int i=x;i>0;i-=i&(-i)) ans=max(ans,t[i]);
	return ans;
}

void prework()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&b[i]);
	for (int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		a[i]*=(LL)b[i];
	}
	for (int i=1;i<=n;i++)
	{
		if (i+b[i]<=n) num[i+b[i]].push_back(i);
	}
}

int main()
{
	freopen("fc.in","r",stdin);
	freopen("fc.out","w",stdout);
	prework();
	for (int i=1;i<=n;i++)
	{
		for (int j=0;j<num[i].size();j++)
		{
			int x=num[i][j];
			updata(x,f[x]);
	    }
	    f[i]=getmax(max(i-b[i],0))+a[i];
	    ans=max(ans,f[i]);
	}
	printf("%lld\n",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值