P7302 [NOI1998] 免费的馅饼
P7302 [NOI1998] 免费的馅饼
题目描述
SERKOI 最新推出了一种叫做“免费馅饼”的游戏:游戏在一个舞台上进行。舞台的宽度为
游戏开始后,从舞台天幕顶端的格子中不断出现馅饼并垂直下落。游戏者左右移动去接馅饼。游戏者每秒可以向左或向右移动一格或两格,也可以站在原地不动。
当馅饼在某一时刻恰好到达游戏者所在的格子中,游戏者就收集到了这块馅饼。当馅饼落在一个游戏者不在的格子里时该馅饼就消失。
写一个程序,帮助我们的游戏者收集馅饼,使得所收集馅饼的分数之和最大。
提示
对于
Solution:
有趣 dp 题。
首先我们不难想到拆绝对值:
令
然后我们整理一下:
也就是说,如果上述两种情况都被满足了,那么这个
即只要满足
然后怎么做?当然是线段树优化 dp 了,我们先对所有点以
然后这题就做完了
Code:
#include<bits/stdc++.h> const int N=1e5+5; const int inf=3e8; using namespace std; inline int Max(int x,int y){return x > y ? x : y;} inline int Min(int x,int y){return x < y ? x : y;} struct Node{ int tim,dis,w; bool operator <(const Node &e)const{ return 2*tim-dis < 2*e.tim-e.dis; } }q[N]; int n,m,cnt; struct Segment_Tree{ int cnt,rt[N]; struct Tree{ int ls,rs,val; }t[N*32]; void pushup(int x){t[x].val=Max(t[t[x].ls].val,t[t[x].rs].val);} void upd(int &x,int l,int r,int pos,int val) { if(!x)x=++cnt; if(l==r){t[x].val=Max(t[x].val,val);return;} int mid=l+r>>1; if(pos<=mid)upd(t[x].ls,l,mid,pos,val); if(mid<pos) upd(t[x].rs,mid+1,r,pos,val); pushup(x); } int query(int x,int l,int r,int L,int R) { if(L<=l&&r<=R){return t[x].val;} int mid=l+r>>1,res=0; if(L<=mid)res=Max(res,query(t[x].ls,l,mid,L,R)); if(mid<R) res=Max(res,query(t[x].rs,mid+1,r,L,R)); return res; } #undef ls #undef rs }T; int f[N]; void work() { cin>>n>>m; for(int i=1;i<=m;i++) { scanf("%d%d%d",&q[i].tim,&q[i].dis,&q[i].w); } sort(q+1,q+1+m); cnt=m; for(int i=1;i<m;i++) { if(q[i].dis==q[i+1].dis&&q[i].tim==q[i+1].tim)q[i+1].w+=q[i].w,q[i]={inf,inf,inf},cnt--; } sort(q+1,q+1+cnt); for(int i=1;i<=cnt;i++) { f[i]=q[i].w; f[i]=T.query(T.rt[0],-inf,inf,-inf,2*q[i].tim+q[i].dis)+q[i].w; T.upd(T.rt[0],-inf,inf,2*q[i].tim+q[i].dis,f[i]); //cout<<"pos:"<<2*q[i].tim-q[i].dis<<" "<<2*q[i].tim+q[i].dis<<"="<<f[i]<<"\n"; } int ans=0; for(int i=1;i<=cnt;i++)ans=Max(ans,f[i]); printf("%d", ans); } int main() { //freopen("P7302_3.in","r",stdin);freopen("P7302.out","w",stdout); work(); return 0; }