您的位置:首页 > 其它

[bzoj2131] 免费的馅饼

2017-10-25 20:38 190 查看

题目大意

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

n≤100000 W,pi,ti≤108

分析

设f[i]表示最后一个拿到的馅饼是i的答案。

i能转移到j,需要满足的条件是|wi−wj|≤2(tj−ti)

可以把绝对值拆开,变成wi−wj≤2(tj−ti)且wj−wi≤2(tj−ti)。这样是等价的。

移项变成2ti+wi≤2tj+wj和2ti−wi≤2tj−wj,那么可以按其中一个的升序顺序枚举,并离散化另一个,用树状数组维护前缀最大值来做。

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
,f
,ans;

set <int> t;

map <int,int> id;

struct Data
{
int t,p,v;
}A
;

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: