題解-[Violet]天使玩偶/SJY擺棋子
阿新 • • 發佈:2021-10-22
本蒟蒻先想了一個naive的\(O(n\log^3{n})\)做法。
看到求最小值可以想到先二分答案,然後用一個“集體二分”(我也不知道是什麼鬼就是一起做二分,然後每個詢問的二分上下界都不同)的方法可以一起二分,二分到一個答案後只需統計這個範圍內有沒有點即可。這時我們可以把曼哈頓距離轉成切比雪夫距離,然後就套一個cdq的模板就做完了。可以證明每次cdq的時間複雜度為 \(O(n\log^2{n})\),然後外層有 \(O(\log{n})\) 次迴圈,所以總時間複雜度為恥辱的 \(O(n\log^3{n})\)。
顯然是過不去的。考慮換一種思路,不二分。
這時處理曼哈頓距離的第二種套路:拆絕對值。加上偏序關係後就可以把絕對值拆開了,對應四種關係,對於每種用一個cdq來求即可。這裡cdq不再是計數,而是統計一個最大值,具體看程式碼。
這裡還是舉個栗子:假設我們拆開是這樣的 \(|x1-x2|+|y1-y2|=(x1+y1)-(x2+y2)\),那麼我們只需維護 \(x2+y2\) 的最大值即可。這個用樹狀陣列也可以維護。
考慮其他偏序關係,只需改變偏序關係即可(不用搞什麼座標軸旋轉一坨迷惑行為)。
這題卡常,所以最好把O2帶上(我也不知道O2為什麼會快這麼多)。
#include <bits/stdc++.h> using namespace std; const int MAXV = 1000010; const int inf = 0x3f3f3f3f; struct node{ int x, y, val, id, ans; }p[1000005], tmp[1000005], q[1000005]; int ans[1000005]; int n, m, tot, cnt; int BIT[1000055]; int dx[5] = {0, 1, 1, -1, -1}; int dy[5] = {0, 1, -1, 1, -1}; int ask(int x) { int ret = -inf; while (x) { ret = max(ret, BIT[x]); x -= (x & (-x)); } return ret; } void add(int x, int v) { while (x <= MAXV) { BIT[x] = max(BIT[x], v); x += (x & (-x)); } } void del(int x) { while (x <= MAXV) { BIT[x] = -inf; x += (x & (-x)); } } bool cmp(int id, int x1, int x2) { if (id <= 2) return x1 > x2; return x1 < x2; } void cdq(int id, int l, int r) { if (l == r) return; int mid = (l + r) >> 1; cdq(id, l, mid); cdq(id, mid + 1, r); int i = l, j = mid + 1, top = 0; while (i <= mid && j <= r) { if (cmp(id, p[i].x, p[j].x)) { if (id & 1) p[j].ans = min(p[j].ans, p[j].x * dx[id] + p[j].y * dy[id] - ask(p[j].y + 1)); else p[j].ans = min(p[j].ans, p[j].x * dx[id] + p[j].y * dy[id] - ask(MAXV - p[j].y - 1)); tmp[++top] = p[j++]; } else { if (!p[i].val) { tmp[++top] = p[i++]; continue; } if (id & 1) add(p[i].y + 1, p[i].x * dx[id] + p[i].y * dy[id]); else add(MAXV - p[i].y - 1, p[i].x * dx[id] + p[i].y * dy[id]); tmp[++top] = p[i++]; } } while (i <= mid) { if (!p[i].val) { tmp[++top] = p[i++]; continue; } if (id & 1) add(p[i].y + 1, p[i].x * dx[id] + p[i].y * dy[id]); else add(MAXV - p[i].y - 1, p[i].x * dx[id] + p[i].y * dy[id]); tmp[++top] = p[i++]; } while (j <= r) { if (id & 1) p[j].ans = min(p[j].ans, p[j].x * dx[id] + p[j].y * dy[id] - ask(p[j].y + 1)); else p[j].ans = min(p[j].ans, p[j].x * dx[id] + p[j].y * dy[id] - ask(MAXV - p[j].y - 1)); tmp[++top] = p[j++]; } for (int now = l; now <= mid; now++) { if (!p[now].val) continue; if (id & 1) del(p[now].y + 1); else del(MAXV - p[now].y - 1); } for (int now = 1; now <= top; now++) p[l + now - 1] = tmp[now]; } int main() { memset(BIT, -inf, sizeof(BIT)); memset(ans, inf, sizeof(ans)); scanf("%d%d", &n, &m); tot = n; for (int i = 1; i <= n; i++) scanf("%d%d", &p[i].x, &p[i].y), p[i].val = 1; while (m--) { int opt, x, y; scanf("%d%d%d", &opt, &x, &y); if (opt == 1) p[++tot] = node{x, y, 1, 0, 0}; else p[++tot] = node{x, y, 0, ++cnt, 0}; } for (int i = 1; i <= tot; i++) p[i].ans = inf; for (int i = 1; i <= tot; i++) q[i] = p[i]; cdq(1, 1, tot); for (int i = 1; i <= tot; i++) ans[p[i].id] = min(ans[p[i].id], p[i].ans); for (int i = 1; i <= tot; i++) p[i] = q[i]; cdq(2, 1, tot); for (int i = 1; i <= tot; i++) ans[p[i].id] = min(ans[p[i].id], p[i].ans); for (int i = 1; i <= tot; i++) p[i] = q[i]; cdq(3, 1, tot); for (int i = 1; i <= tot; i++) ans[p[i].id] = min(ans[p[i].id], p[i].ans); for (int i = 1; i <= tot; i++) p[i] = q[i]; cdq(4, 1, tot); for (int i = 1; i <= tot; i++) ans[p[i].id] = min(ans[p[i].id], p[i].ans); for (int i = 1; i <= cnt; i++) printf("%d\n", ans[i]); return 0; }