【YBTOJ】【Luogu P7302】[NOI1998]免费馅饼

链接:

题目

博客园

题目大意:

(n) 个馅饼,第 (i) 个馅饼在 (t_i) 时掉落在 (p_i) 上,若掉落时玩家不没接住,馅饼就会消失。接住第 (i) 个馅饼能获得 (v_i) 的分数,问如何使得分数之和最大。

正文:

考虑用动态规划。设 (f_i) 表示前 (i) 个馅饼接住了第 (i) 个的最大分数。我们可以列方程得:

[f_i=max_{ ext{abs}(p_i-p_j)leq 2(t_i-t_j)}{f_j+v_i} ]

直接 (mathcal{O}(n^2)) 是不可行的,考虑优化,展开上式的条件:

[left{egin{matrix} p_i+2t_ileq p_j+2t_j&(p_ileq p_j)\ p_i-2t_ileq p_j-2t_j&(p_i> p_j) end{matrix} ight.]

因为有两个关键字,按照关于第一个式子的关键字排序,用关于第二个式子的关键字树状数组维护就好了(或者用第二个式子排序,第一个式子树状数组,顺序没影响)。

代码:

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
", query(k));
	
	return 0;
}
原文地址:https://www.cnblogs.com/GJY-JURUO/p/14336793.html