Atcoder Grand Contest 023D

Atcoder Grand Contest 023D - Go Home

题目大意

  • 一条路上有 N N N幢房子,每幢房子住有 X i X_i Xi个人,最初汽车从某个地点 S S S出发,装着所有的人,汽车每次走一个单位,向左或向右由所有人投票决定,少数服从多数,如果汽车到达了自己所住的房子则下车。
  • 每个人都会尽可能使自己尽早到达,求最晚到达的人的到达时间。
  • N ≤ 1 0 5 N≤10^5 N105

题解

  • 第一眼以为维护前缀和后缀和,每次最最贪心地判断左右哪边人多久往哪边走,但一看样例就会发现这样是错的,
  • 也就是说,对于每个人而言,不一定每次都向着自己想去的地方投票就一定能更早到达。
  • 举个例子,加入当前左边依次有两幢房子,人数为 A , B A,B A,B,右边一幢房子人数为 C C C
  • 满足 A < C A<C A<C B < C B<C B<C A + B > C A+B>C A+B>C
  • 考虑 A A A中的人会如何投票?
  • 如果 A A A向左投, B B B也向左投, C C C会向右投,那么汽车会向左移动,可是并不能一直移动到 A A A
  • 直到 B B B的人下车后,向左的人数就小于向右的人数了,那么汽车右会一直移动到最右边再回来到 A A A
  • 因此 A A A中的人会先投右边, 让 C C C先到,他们自己便也可以更早到达。
  • 这是为什么呢?考虑更一般的情况:
  • 若当前最左边的人数为 A A A,最右边的人数为 B B B,满足 A < B A<B A<B,中间大小不考虑( A > B A>B A>B同理),
  • 如果汽车一直向左走,虽然会不断靠近 A A A,但总会有一个时刻,向左的人数小于向右的人数了,而使汽车向右走去,绕到最右端才回来,
  • 这样看的话,若 A < B A<B A<B,则 A A A一定会比 B B B更晚到,且 B B B到达之后,再到达 A A A之间所用的时间是确定的,即为二者之间的距离,那么 A A A为了自己先到,会尽可能让 B B B先到,所以 A A A会向右投票,
  • 于是,可以忽略 A A A的存在,直接给 B B B加上 A A A,然后总时间加上二者之间的距离,于是剩下的部分转为原问题的一个子问题,按同样的思路继续求解,只需要记录当前子问题的区间 [ l , r ] [l,r] [l,r],可以用递归实现,复杂度 O ( n ) O(n) O(n)
  • 在这个过程中,相当于推出汽车运动的反向过程,给 B B B加上 A A A相当于汽车有一个 B B B A A A的移动过程。
  • 那边界条件是什么?
  • 根据题意,如果子问题的整个区间在 S S S的左侧或右侧了,那么所有人的意见就会统一进行,按顺序直接走完。
  • 要注意,如果当前子问题中汽车的移动反向和上一次相同的话,那么不用计入答案,因为这一次的答案会被上一次包含。

代码

#include<cstdio> 
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100010
ll ans = 0;
int t;
struct {
	int x; ll s;
}a[N];
void ct(int l, int r, int o) {
	if(a[l].x >= t) {
		ans += a[r].x - t;
		return;
	}
	if(a[r].x <= t) {
		ans += t - a[l].x;
		return;
	}
	if(a[l].s >= a[r].s) {
		a[l].s += a[r].s;
		if(o != 0) ans += a[r].x - a[l].x;
		ct(l, r - 1, 0);
	}
	else {
		a[r].s += a[l].s;
		if(o != 1) ans += a[r].x - a[l].x;
		ct(l + 1, r, 1);
	}
}
int main() {
	int n, i;
	scanf("%d%d", &n, &t);
	for(i = 1; i <= n; i++) scanf("%d%lld", &a[i].x, &a[i].s);
	ct(1, n, -1);
	printf("%lld", ans);
	return 0;
}
哈哈哈哈哈哈哈哈哈哈
原文地址:https://www.cnblogs.com/LZA119/p/13910024.html