[bzoj2131] 免费的馅饼

Description

Input

第一行是用空格隔开的二个正整数,分别给出了舞台的宽度W(1到10^8之间)和馅饼的个数n(1到10^5)。  接下来n行,每一行给出了一块馅饼的信息。由三个正整数组成,分别表示了每个馅饼落到舞台上的时刻t[i](1到10^8秒),掉到舞台上的格子的编号p[i](1和w之间),以及分值v[i](1到1000之间)。游戏开始时刻为0。输入文件中同一行相邻两项之间用一个空格隔开。输入数据中可能存在两个馅饼的t[i]和p[i]都一样。

Output

一个数,表示游戏者获得的最大总得分。

Sample Input

3 4
1 2 3
5 2 3
6 3 4
1 1 5

Sample Output

12
【数据规模】
对于100%的数据,1<=w,t[i]<=10^8,1<=n<=100000。
 
这道题真的令我印象十分特别深刻...。 在东北的时候听过 回来又听zyl讲过
这道题是一道用线段树解决的二位偏序的$dp$问题
dp[i]表示当前吃第$i$块馅饼所能够获得的最大价值 考虑如何转移 转移方程式是很简单的
$dp[i] = max(dp[j] + val[i])$,$j$属于能够转移到$i$的馅饼
那么现在需要解决的问题就是哪些馅饼能够转移到$i$号 首先$j$号馅饼一定要在$i$号馅饼之前出现
考虑如果$j$号馅饼能够转移到$i$号   那么还需要满足式子
$|pos[i] - pos[j]| <= 2 * (t[i] - t[j])$ 
打开绝对值之后得到的两个化简得到两个关于$i,j$的式子 将同属性的移到等式一边得到
        $2 * t[i] - pos[i] >= 2 * t[j] - pos[j]  , 2 * t[i]  + pos[i] >= 2 * t[j] + pos[j]$
发现这两个式子是等价的 也就是说只要同时满足上面两个式子 原始式子就一定成立
证明:
$1.pos[i] >= pos[j]$ 也就是说他满足了式子$1$ 原本$t[i] > t[j]$     那么$2 * t[i]$加上$pos[i]$后会比左边更大  一定成立
第二种情况的证明是一样的 所以两只需要同时满足这两个属性就可以进行转移
代码
#include <bits/stdc++.h>
using namespace std;
 
const int N = 1e5 + 5;
int n,m,tt[N],f[4 * N],w,dp[N];
 
struct food {
     
    int f1,f2,val;
}b[N];
 
void init( ) {
     
    int t,p,val;
    scanf("%d%d",& w,& n);
    for(int i = 1;i <= n;i ++) {
        scanf("%d%d%d",& t,& p,& val);
        b[i].f1 = 2 * t + p;
        b[i].f2 = 2 * t - p;
        b[i].val = val;
        tt[i] = 2 * t + p;
    }
    sort(tt + 1,tt + n + 1);
    m = unique(tt + 1,tt + n + 1) - tt - 1;
}
 
void update(int o) {
     
    f[o] = max(f[2 * o],f[2 * o + 1]);
}
 
bool cmp(const food & a,const food & b) {
     
    return a.f2 < b.f2;
}
 
int query(int o,int l,int r,int L,int R) {
     
    if(l >= L && r <= R) return f[o];
    int mid = (l + r) >> 1;
    int ans = 0;
    if(L <= mid) ans = max(ans,query(2 * o,l,mid,L,R));
    if(mid < R)  ans = max(ans,query(2 * o + 1,mid + 1,r,L,R));
    return ans;
}
 
void modify(int o,int l,int r,int pos,int val) {
     
    if(l == r) {
        f[o] = max(f[o],val);
        return ;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) modify(2 * o,l,mid,pos,val);
    else modify(2 * o + 1,mid + 1,r,pos,val);
    update(o);
}
 
int main( ) {
     
    init( );
    sort(b + 1,b + n + 1,cmp);
     
    for(int i = 1;i <= n;i ++) {
        int pos = lower_bound(tt + 1,tt + m + 1,b[i].f1) - tt;
        int q = query(1,1,m,1,pos);
        dp[i] = q + b[i].val;
        modify(1,1,m,pos,dp[i]);
    }
    printf("%d",query(1,1,m,1,m));
    return 0;
}
posted @ 2018-10-12 17:20  阿澈说他也想好好学习  阅读(334)  评论(0编辑  收藏  举报