【線段樹優化建圖】luogu_P4083 [USACO17DEC]A Pie for a Pie G
阿新 • • 發佈:2020-08-20
題意
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]); }