【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;
}
posted @ 2021-01-27 20:27  Jayun  阅读(80)  评论(0编辑  收藏  举报