【YBTOJ】【Luogu P7302】[NOI1998]免费馅饼
链接:
题目大意:
有 \(n\) 个馅饼,第 \(i\) 个馅饼在 \(t_i\) 时掉落在 \(p_i\) 上,若掉落时玩家不没接住,馅饼就会消失。接住第 \(i\) 个馅饼能获得 \(v_i\) 的分数,问如何使得分数之和最大。
正文:
考虑用动态规划。设 \(f_i\) 表示前 \(i\) 个馅饼接住了第 \(i\) 个的最大分数。我们可以列方程得:
\[f_i=\max_{\text{abs}(p_i-p_j)\leq 2(t_i-t_j)}\{f_j+v_i\}
\]
直接 \(\mathcal{O}(n^2)\) 是不可行的,考虑优化,展开上式的条件:
\[\left\{\begin{matrix}
p_i+2t_i\geq p_j+2t_j&(p_i\leq p_j)\\
p_i-2t_i\leq p_j-2t_j&(p_i> p_j)
\end{matrix}\right.\]
因为有两个关键字,按照关于第一个式子的关键字排序,用关于第二个式子的关键字树状数组维护就好了(或者用第二个式子排序,第一个式子树状数组,顺序没影响)。
代码:
const int N = 1e5 + 10;
ll t[N], b[N];
int n, m, k;
void modify(int x, ll val){for (; x <= k; x += x & -x) t[x] = max(t[x], val);}
ll query (int x)
{
ll ans = 0;
for (; x; x -= x & -x) ans = max(ans, t[x]);
return ans;
}
struct node
{
ll l, r, p, t;
int v;
}a[N];
bool cmp1 (node a, node b) {return a.r < b.r;}
bool cmp2 (node a, node b) {return a.l < b.l;}
int main ()
{
scanf ("%d%d", &m, &n);
for (int i = 1; i <= n; i++)
scanf ("%d%d%d", &a[i].t, &a[i].p, &a[i].v),
a[i].l = 2 * a[i].t - a[i].p,
a[i].r = 2 * a[i].t + a[i].p,
b[i] = a[i].r;
sort (a + 1, a + 1 + n, cmp1);
sort (b + 1, b + 1 + n);
k = unique(b + 1, b + 1 + n) - b;
for (int i = 1; i <= n; i++)
a[i].r = lower_bound(b + 1, b + 1 + n, a[i].r) - b;
sort (a + 1, a + 1 + n, cmp2);
for (int i = 1; i <= n; i++)
{
ll tmp = query(a[i].r);
modify(a[i].r, tmp + a[i].v);
}
printf ("%lld\n", query(k));
return 0;
}