启智树冲省队组Day4T3 疏散

一栋大楼中 \(10^5\) 个人紧急疏散。大楼可以被视作网格图(正视图),有三个区域,由两个宽为 1 的楼道隔开。走出楼道最下面即为离开。同一时刻一个位置只能有一个人。左右区域的人不能互相串。每个人每一秒能移动一格。问所有人离开所需的最小时间

这题正解比暴力好写(一般贪心题都这样),因此考场上没想到贪心就很难受。

首先考虑中间没有人的情况。这是个经典套路了。我们可以把每个人单独的花费时间算出来,然后如果花费时间相同,则它们会冲突,需要让一个人的时间 + 1;如果还出现相同,就继续 + 1,直到没有相同的为止。这个可以用并查集来维护。

然后考虑中间有人的情况。如果人非常少,那么我们可以暴力枚举每个人去左边还是去右边。

现在考虑一般情况。考虑到这道题实际上是要求“最晚走的人的走的时间最小”,于是考虑二分答案,保证每个人的时间都在 \(mid\) 以内即可。我们设 \(L_i\) 表示第 \(i\) 个人去左边的时间,\(R_i\) 表示第 \(i\) 个人去右边的时间。然后我们将所有人按照 \(R_i\) 从大到小排序,如果能去左边就尽量去左边,否则去右边

感性证明:显然时间小更优,那么以后的所有人对于“去右边”都比当前这个人优。如果能够把当前这个人安排到左边,那么就算以后有个人也想安排到左边被占了,他还可以来右边,并且肯定比当前这个人的选择更多。

看来,“想不到怎么贪心就排个序”的方法还是挺有用的。

另外,此题的离散化也需要一些小技巧。暴力的 \(n^2\) 离散化显然不行,直接用 \(map\) 也感觉多个 \(log\) 不太稳(或许也可以手写 unordered_map)。考虑到有用的值只可能是计算出来的时间或者计算出来的时间 + 一些人数。我们可以把最劣情况模拟一遍,搞出所有的有用时间点,并且中间还需要空一些格子。显然,这样的时间点数量不会超过 \(4n\)。不太好描述,直接看代码吧:

inline void lsh() {
	sort(h + 1, h + 1 + htot);
	int nw = 0;
	for (register int i = 1; i <= htot; ++i) {
		if (h[i] <= nw + 1)	++nw, lh[++ltot] = nw;
		else	++nw, lh[++ltot] = nw, nw = h[i], lh[++ltot] = nw;
	}
	for (register int i = 1; i <= n; ++i)
		nd[i].t1 = lower_bound(lh + 1, lh + 1 + ltot, nd[i].t1) - lh,
		nd[i].t2 = lower_bound(lh + 1, lh + 1 + ltot, nd[i].t2) - lh;
}
posted @ 2020-08-25 15:39  JiaZP  阅读(184)  评论(0编辑  收藏  举报