[bzoj2131] 免费的馅饼

题目大意

你在一个1*W的格子图上,最开始可以在任意位置。每一个时刻你可以向左右移动1或2格,也可以不动。第i个馅饼在ti时刻落在位置pi上,价值为vi。问你可以获得的最大价值和是多少。

n≤100000 W,pi,ti≤ 108

分析

设f[i]表示最后一个拿到的馅饼是i的答案。
i能转移到j,需要满足的条件是 |wiwj|2(tjti)
可以把绝对值拆开,变成 wiwj2(tjti) wjwi2(tjti) 。这样是等价的。
移项变成 2ti+wi2tj+wj 2tiwi2tjwj ,那么可以按其中一个的升序顺序枚举,并离散化另一个,用树状数组维护前缀最大值来做。

O(nlogn)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>

#define max(a,b) ((a)>(b)?(a):(b))

using namespace std;

const int N=1e5+5;

typedef long long LL;

int W,n,m,g[N],f[N],ans;

set <int> t;

map <int,int> id;

struct Data
{
    int t,p,v;
}A[N];

bool operator < (Data a,Data b)
{
    return a.t*2-a.p<b.t*2-b.p;
}

void Add(int x,int y)
{
    for (;x<=n;x+=x&-x) g[x]=max(g[x],y);
}

int getmax(int x)
{
    int k=0;
    for (;x>0;x-=x&-x) k=max(k,g[x]);
    return k;
}

int main()
{
    scanf("%d%d",&W,&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&A[i].t,&A[i].p,&A[i].v);
        t.insert(A[i].t*2+A[i].p);
    }
    sort(A+1,A+n+1);
    for (set <int> ::iterator it=t.begin();it!=t.end();it++) id[*it]=++m;
    for (int i=1;i<=n;i++)
    {
        ans=max(ans,f[i]=getmax(id[A[i].t*2+A[i].p])+A[i].v);
        Add(id[A[i].t*2+A[i].p],f[i]);
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值