【线段树优化建图】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]);
}

浙公网安备 33010602011771号