题目大意
你在一个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[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;
}