的士碰撞(二分答案)

Description

𝑛辆车在一条数轴上,车的编号为1到𝑛。编号为𝑖的车坐标为𝑎[𝑖],初始方 向为𝑑𝑖𝑟[𝑖](左或右),初始位置两两不同。每辆车每个时刻行走距离为1。两辆 车相碰时,会调转方向,继续行走,掉头不消耗时间。现在车子开始朝其方向行 驶,同一个坐标允许有多辆车。现在有𝑞个询问,给出𝑡, 𝑖,询问过了𝑡时刻后, 编号为𝑖的车的坐标的绝对值。

Hint

𝑛, 𝑞 ≤ 100000,0 ≤ 𝑎[𝑖] ≤ 109 ,𝑡 ≤ 109 , 𝑑𝑖𝑟[𝑖] ∈ {0,1}

Solution

可以发现整体的排名是不变的,只是位置发生变化,

正解是二分答案,在坐标轴上二分一个位置,这个位置是t秒后编号为x的车的坐标

因为rank不会变,我们可以找出这个位置前面有多少辆车,假设有m个,它的排名就是m+1,就可以验证了

但是如何找出前面有多少车呢?线性查找会超时,采取二分查找即可

这里要分左右两种情况讨论,分别预处理出两个方向的所有车的初始位置

例如是从左边过来的,那么方向向右,初始位置mid-t,然后二分查找出前面有几辆车,

右边差不多,具体见代码

记得位置要用long long

这里感谢Shawn_xc学长的指导

Code

#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long
#define N 100010
using namespace std;

struct info {
	int pos, id, d;
} a[N], tmp[N];
int n, q, t, rank[N], nl, nr;
LL L[N], R[N];

inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while (ch < '0' || ch > '9') {if (ch == '-')f = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * f;
}
bool cmp(info a, info b) {return a.pos < b.pos;}

int Count(LL *A, int len, LL p) {
	int res = 0, l = 1, r = len;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (A[mid] <= p)
			res = mid, l = mid + 1;
		else r = mid - 1;
	}
	return res;
}

int main() {
	n = read(), q = read();
	for (int d, i = 1; i <= n; ++i) {
		a[i].id = i;
		a[i].pos = read();
		d = read();
		if (d == 0) L[++nl] = a[i].pos;
		else R[++nr] = a[i].pos;
	}
	sort(a + 1, a + n + 1, cmp);
	for (int i = 1; i <= n; ++i)
		rank[a[i].id] = i;
	sort(L + 1, L + nl + 1);
	sort(R + 1, R + nr + 1);

	while (q--) {
		int t = read(), x = read(), Ans;
		x = rank[x];
		LL l = min(L[1] - t, R[1] + t), r = max(L[nl] - t, R[nr] + t);
		while (l <= r) {
			LL mid = (l + r) >> 1;
			int cnt = Count(L, nl, mid + t) + Count(R, nr, mid - t);
			if (cnt >= x)
				Ans = mid, r = mid - 1;
			else l = mid + 1;
		}
		printf("%d\n", (int)fabs(Ans));
	}
	return 0;
}
posted @ 2017-10-23 09:35  void_f  阅读(287)  评论(4编辑  收藏  举报