P5867 [SEERC2018] Fishermen(暂无评定) 题解

发布时间 2023-11-25 22:03:40作者: BadBadBad__gqc

题意

\(n\) 条鱼,\(m\) 个渔夫,且这 \(m\) 个渔夫都在横坐标轴上,每个渔夫都有一个长度为 \(l\) 的鱼竿,当鱼和渔夫距离小于或等于 \(l\) 时,鱼能被钓到。

并且渔夫 \((x,0)\) 与鱼 \((a,b)\) 的距离(假设为 \(L\) )满足如下公式 \(|a − x| + b\) 式子中 \(x\) 为渔夫的横坐标,\((a,b)\) 为鱼的坐标。

求解思路

这道题肯定以鱼为考虑的对象。先对公式 \(|a - x| + b\) 入手。设 \(l ≤ |a - x| + b\)

可求出鱼能被钓到时渔夫 \(x\) 的一个范围 \(a+l-b \leq x \leq a-l+b\)

然后把渔夫的位置排个序,遍历所有的鱼,每一条鱼能够被可以被抓都在 \(x\) 轴有一个范围 \((a+l-b \leq x \leq a-l+b)\)

通过二分,在渔夫中找到最左的和最右的,然后差分一下,最后求个前缀和就可以得出答案。

代码

#include <bits/stdc++.h>
using namespace std;
typedef  long long ll;
const int maxn = 2e5 + 7;
struct fish {
	int x, y;
} f[maxn];
struct yufu {
	int x, id, idd; ///id表示输入时的序号。idd表示排序之后的序号
	bool operator <(const yufu  &bb)const { ///重载<运算符 为了能够用sort进行排序
		return x < bb.x;
	}
} ma[maxn];
int sum[maxn], ans[maxn];
int main() {
	int n, m, l;
	scanf("%d %d %d", &n, &m, &l);
	memset(sum, 0, sizeof(sum));
	for (int i = 1; i <= n; i++)
		scanf("%d %d", &f[i].x, &f[i].y);
	for (int i = 1; i <= m; i++) {
		scanf("%d", &ma[i].x);
		ma[i].id = i;
	}
	sort(ma + 1, ma + m + 1); ///排序
	for (int i = 1; i <= m; i++)
		ma[i].idd = i;
	for (int i = 1; i <= n; i++) {
		if (f[i].y - l > 0) continue; ///当距离超过l时continue掉
		yufu  tmp;
		tmp.x = f[i].x - l + f[i].y; ///二分法
		int xx = lower_bound(ma + 1, ma + m + 1, tmp) - ma; ///注意一点用这两个函数时,tmp和ma必须是同类型的
		tmp.x = f[i].x + l - f[i].y;
		int yy = upper_bound(ma + 1, ma + m + 1, tmp) - ma;
		sum[xx]++;
		sum[yy]--;
	}
	for (int i = 1; i <= m; i++) { ///用差分法求前缀和
		sum[i] += sum[i - 1];
		ans[ma[i].id] = sum[ma[i].idd];
	}
	for (int i = 1; i <= m; i++) printf("%d\n", ans[i]);
	return 0;
}