【线段树优化建图】luogu_P4083 [USACO17DEC]A Pie for a Pie G

题意

2个人各自有n个礼物。
每个人从对方收到礼物,会回送在自己眼中价值\(x\sim x+d\)的礼物,其中\(x\)是对方送的礼物在自己眼中的价值。
当有人收到对方的礼物在自己眼中价值为0,那么送礼结束。
对于第1个人的每种礼物,求出从它开始送起之后最少送礼的次数使送礼结束。

思路

可以反过来思考这个问题,就是从价值0的礼物开始跑最短路。
那么对于一个礼物,它是指向一个区间的,我们就用线段树优化建图。
两边排序初始化即可,详见代码。

代码

#include <queue>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct node {
	int a, b, id;
}g[200001];
int n, d, tot;
int b[200001], ans[100001];
int ver[1000001], next[1000001], edge[1000001], head[1000001], dis[1000001], v[1000001];

int cmp1(node x, node y) {
	return x.b < y.b;
}

int cmp2(node x, node y) {
	return x.a < y.a;
}

void add(int u, int v, int w) {
	ver[++tot] = v;
	next[tot] = head[u];
	edge[tot] = w;
	head[u] = tot;
}

void build(int p, int l, int r) {
	if (l == r) {
		b[l] = p;
		return;
	}
	int mid = l + r >> 1;
	add(p, p << 1, 0);
	add(p, p << 1 | 1, 0);
	build(p << 1, l, mid);
	build(p << 1 | 1, mid + 1, r);
}

int find1(int val) {
	int l = n + 1, r = 2 * n;
	if (g[r].a < val) return -1;
	while (l < r) {
		int mid = l + r >> 1;
		if (val <= g[mid].a) r = mid;
		else l = mid + 1;
	}
	return l;
}

int find2(int val) {
	int l = 1, r = n;
	if (g[r].b < val) return -1;
	while (l < r) {
		int mid = l + r >> 1;
		if (val <= g[mid].b) r = mid;
		else l = mid + 1;
	}
	return l;
}

int find3(int val) {
	int l = n + 1, r = 2 * n;
	if (g[l].a > val) return -1;
	while (l < r) {
		int mid = l + r + 1 >> 1;
		if (g[mid].a <= val) l = mid;
		else r = mid - 1;
	}
	return l;
}

int find4(int val) {
	int l = 1, r = n;
	if (g[l].b > val) return -1;
	while (l < r) {
		int mid = l + r + 1 >> 1;
		if (g[mid].b <= val) l = mid;
		else r = mid - 1;
	}
	return l;
}

void modify(int p, int l, int r, int L, int R, int pos) {
	if (L <= l && r <= R) {
		add(pos, p, 1);//连向一个区间
		return;
	}
	int mid = l + r >> 1;
	if (L <= mid) modify(p << 1, l, mid, L, R, pos);
	if (R > mid) modify(p << 1 | 1, mid + 1, r, L, R, pos);
}

void spfa() {
	memset(dis, 127 / 3, sizeof(dis));
	queue<int> q;
	for (int i = 1; i <= n; i++)
		if (!g[i].b && !v[b[i]]) q.push(b[i]), dis[b[i]] = 1, v[b[i]] = 1;
	for (int i = n + 1; i <= 2 * n; i++)
		if (!g[i].a && !v[b[i]]) q.push(b[i]), dis[b[i]] = 1, v[b[i]] = 1;
	while (q.size()) {
		int u = q.front();
		q.pop();
		v[u] = 0;
		for (int i = head[u]; i; i = next[i])
			if (dis[ver[i]] > dis[u] + edge[i]) {
				dis[ver[i]] = dis[u] + edge[i];
				if (!v[ver[i]]) {
					v[ver[i]] = 1;
					q.push(ver[i]);
				}
			}
	}
	for (int i = 1; i <= n; i++)
		ans[g[i].id] = dis[b[i]];
}

int main() {
	scanf("%d %d", &n, &d);
	for (int i = 1; i <= 2 * n; i++)
		scanf("%d %d", &g[i].a, &g[i].b), g[i].id = i;
	sort(g + 1, g + n + 1, cmp1);
	sort(g + n + 1, g + 2 * n + 1, cmp2);
	build(1, 1, 2 * n);
	for (int i = 1; i <= n; i++) {//第一个人连向第二个人
		int l = find1(g[i].a - d), r = find3(g[i].a);
		if (l < 0 || r < 0) continue;
		modify(1, 1, 2 * n, l, r, b[i]);
	}
	for (int i = n + 1; i <= 2 * n; i++) {//反之
		int l = find2(g[i].b - d), r = find4(g[i].b);
		if (l < 0 || r < 0) continue;
		modify(1, 1, 2 * n, l, r, b[i]);
	}
	spfa();
	for (int i = 1; i <= n; i++)
		printf("%d\n", ans[i] == 707406378 ? -1 : ans[i]);
}
posted @ 2020-08-20 21:18  nymph181  阅读(243)  评论(0)    收藏  举报