1. 程式人生 > 其它 >題解-[Violet]天使玩偶/SJY擺棋子

題解-[Violet]天使玩偶/SJY擺棋子

本蒟蒻先想了一個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;
}