P8272

[USACO22OPEN] Apple Catching G

题目描述

天上下苹果了!在某些时刻,一定数量的苹果会落到数轴上。在某些时刻,Farmer John 的一些奶牛将到达数轴并开始接苹果。

如果一个苹果在没有奶牛接住的情况下落到数轴上,它就会永远消失。如果一头奶牛和一个苹果同时到达,奶牛就会接住苹果。每头奶牛每秒可以移动一单位距离。一旦一头奶牛接住了一个苹果,她就会离开数轴。

如果 FJ 的奶牛以最优方式合作,她们总共能接住多少个苹果?

输入格式

输入的第一行包含 N1N2105),为苹果落到数轴上的次数或 FJ 的奶牛出现的次数。

以下 N 行每行包含四个整数 qitixiniqi{1,2},0ti109,0xi109,1ni103)。

  • 如果 qi=1,意味着 FJ 的 ni 头奶牛在 ti 时刻来到数轴上的 xi 位置。
  • 如果 qi=2,意味着 ni 个苹果在 ti 时刻落到了数轴上的 xi 位置。

输入保证所有有序对 (ti,xi) 各不相同。


模拟赛被创死,展现出普及组选手 cfm 强大的普及水准。

什么时候 i 可以接住苹果 a?(tita

  • xixa 时,有 titaxixatixitaxa
  • xi<xa 时,有 titaxaxiti+xita+xa

赛时的时候列出了后面两个式子,但是仍然并不会这道题。

我们注意到后面两个式子一定同时成立!这一点在移向前的式子非常明显,因为 tita|xixa|。但是移向后似乎就没有这么显然了(upd:其实也非常显然,上下两式相加得到 tita……,反过来只要减一下就好了……)

因此令 tixi=xi,ti+xi=yi,那么 i 可以接住苹果 a 的充要条件就是 xixa,yiya。(这里的 x 和上面的 x 不是一样的!)这是一个二维偏序问题,但一般的二维偏序是计数,这里是最优化:将数对匹配。

回顾二维偏序的过程,是先对 x 进行排序,然后利用数据结构维护 y。这里我们也按照 x 排序以此消除 xixa 的约束。对于每个奶牛 i 考虑把能吃掉的苹果放到集合 S 中。从 x 小的奶牛往 x 大的奶牛考虑。不考虑删除时这个 S 是在增长的。接下来考虑删除,显然 y 越小的苹果越难被匹配。所以从 y 尽量小的奶牛开始匹配。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5;
struct node {
	int opt, x, y, num;
	bool operator < (const node &other) const {
		return y < other.y;
	}
} Q[N + 10];
bool cmp(node &x, node &y) {
	return x.x > y.x;
}
multiset <node> S;

int n;
vector <node> vec[3];
int main() {
	cin >> n;
	for(int i = 1, t, x; i <= n; i++) {
		cin >> Q[i].opt >> t >> x >> Q[i].num;
		Q[i].x = t - x, Q[i].y = t + x;
		vec[Q[i].opt].push_back(Q[i]);
	}
	
	sort(vec[1].begin(), vec[1].end(), cmp);
	sort(vec[2].begin(), vec[2].end(), cmp);
	
	int ia = 0, sum = 0;
	for(int i = 0; i < vec[1].size(); i++) {
		while(ia < vec[2].size() &&
			vec[2][ia].x >= vec[1][i].x) {
				S.insert(vec[2][ia]);
				ia++;
			}
			
		while(1) {
			if(!vec[1][i].num) break;
			set <node>::iterator it = S.lower_bound(vec[1][i]);
			if(it == S.end()) break;
			
			node tmp = (*it);
			if(tmp.num >= vec[1][i].num) {
				sum += vec[1][i].num;
				tmp.num -= vec[1][i].num;
				vec[1][i].num = 0;
				S.erase(it);
				S.insert(tmp);
				break;
			}
			else {
				sum += tmp.num;
				vec[1][i].num -= tmp.num;
				S.erase(it);
			}
		}
	}
	cout << sum << endl;
}

总结

真的是一个很值得反思反思的题目。

复盘过程

  • 在得到充要条件的一步,因为一般来讲是要根据 x 的大小讨论一下的。因此少前置约束是一个值得的动机。
  • 从另一个角度,以少前置约束出发就是 tita|xixa|,等价于 titaxixa,titaxaxi 同时成立。即 x|y| 等价于 xy,xy。这个变形我之前似乎没有用过,记下来
  • 于是得到了一个类二维偏序的匹配问题。对于一个高维问题,最常用的思想是降维。三位偏序二维偏序如此,高维前缀和如此,高维统计也如此。
    • 高维问题
      • 降维
      • 独立维度
  • 接下来的贪心中,是从“难以匹配”的奶牛和苹果出发。这也启发对于匹配类的贪心问题应该从“难以匹配”的元素出发思考策略。
    • 这是因为一般来说匹配问题不带权(比如这道题万一苹果带权似乎就不好贪心了)。难以匹配的赶紧匹配,容易匹配的放到后面匹配也没关系。这是“决策包容性”。
    • 关于这块似乎还要结合几个线段贪心详细总结总结
posted @   SIXIANG32  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示