1 条题解
-
0
这道题咋没有题解,调了我好久题意:二维平面上有 个点,给定 个询问,每次两个点 、,求距离 更近的点的个数、距离 更近的点的个数以及与 、 距离相同的点的个数。距离为曼哈顿距离,横坐标值域为 ,纵坐标值域为 。
首先发现只需要考虑处理距离 更近的点即可,因为问题的对称性。
以下记点 的横坐标为 ,纵坐标为 。对于 个点中的任意一个点 ,我们列出关系式:$|X_A - X_{P1}| + |Y_A - Y_{P1}| < |X_A - X_{P2}| + |Y_A - Y_{P2}|$。
根据初中数学知识,、 都有 3 种情况,一共 9 种情况,我们把它们在图像中表示出来。假设给定的 ,。

rt,显然区域 2、4、6、8,区域 1、9,区域 3、7,区域 5 为四种不同情况,以下进行分讨:
记 ,
- 区域 2、4、6、8

以区域 8 为例,$\operatorname{dist}(P1,A) = \Delta y + (a - \Delta x) + b$,。
要保证 $\operatorname{dist}(P1,A) < \operatorname{dist}(P2,A)$,即要保证 $\Delta y + (a - \Delta x) + b < \Delta x + \Delta y$,即 ,带入 得 。
因而该区域满足条件的点 需满足 $X_A \in [X_{P1},X_{P2}] \cap [0,X_{P2} - \frac{a + b}{2})$,。
- 区域 1、9

以区域 9 为例,显然 $\operatorname{dist}(P1,A) > \operatorname{dist}(P2,A)$。(当然 P1 = P2 的情况需要特判)
因此该区域所有点均不满足。
- 区域 3、7

以区域 7 为例,$\operatorname{dist}(P1,A) = \Delta x + \Delta y + b$,$\operatorname{dist}(P2,A) = \Delta x + \Delta y + a$。
可以发现当 时 $\operatorname{dist}(P1,A) < \operatorname{dist}(P2,A)$。
所以当 时该区域所有点均满足。
- 区域 5

区域 5 中,$\operatorname{dist}(P1,A) = (a - \Delta x) + (b - \Delta y)$,。
要保证 $\operatorname{dist}(P1,A) < \operatorname{dist}(P2,A)$,即要保证 $(a - \Delta x) + (b - \Delta y) < \Delta x + \Delta y$,即 ,带入 、 得 。
因而该区域满足条件的点 需满足 ,,$X_A + Y_A \in [0, X_{P2} + Y_{P2} - \frac{a + b}{2})$。
到这儿分类讨论结束。
如果 、 不是偏序关系怎么办?一开始我想的是再分讨一次,但是太麻烦,我们可以考虑将整个平面反转。具体的,我们考虑翻转 这个矩形,若是左右翻转则所有横坐标 变为 ,若是上下翻转则所有纵坐标 变为 。
现在尝试维护答案。我们发现前三类只与 , 有关,且均为一段区间,考虑二维偏序,扫描线 + 树状数组即可。
第 4 类(即区域 5)另外有个 的限制,是三维偏序。第一次打的时候用了二维线段树,空间爆炸喜提暴力分,于是专门新学了 KD-Tree 来用。
不幸的是,KD-Tree 常数过大,加上代码自带大常数,无法 AC。
尝试优化。我们在统计答案的时候换一个思路:统计 $\operatorname{dist}(P1,A)<\operatorname{dist}(P2,A)$、与 $\operatorname{dist}(P1,A)\le\operatorname{dist}(P2,A)$ 的点的数量 、。后者只是在原先的推导上,将小于改为小于等于。那么其实它们有很多询问是重叠的,甚至是相同的,可以在相同的时候一块维护。
注意三个答案要变为 ,,。
另外,KD-Tree 的询问速度较慢,可以把询问排一下序,相邻相同的询问使用同一个答案。
最后再加上一些各类广为人知的卡常技巧就可以 AC 啦。
```cpp #include <bits/stdc++.h> #define mem(a, b) memset(a, b, sizeof(a)) #define re register using namespace std; namespace fastio { char *p1, *p2, buf[100000]; #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++) void read(int &x) { x = 0; re int f = 1, ch = nc(); while (ch < 48 || ch > 57) { if (ch == '-') f = -1; ch = nc(); } while (ch >= 48 && ch <= 57) { x = x * 10 + ch - 48; ch = nc(); } } void write(int x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); } } inline const int &min(const int &x, const int &y) { return x < y ? x : y; } inline const int &max(const int &x, const int &y) { return x > y ? x : y; } const int N = 1e5 + 5, M = 8e5 + 5, inf = INT_MAX; struct point { int x, y; }; bool operator==(const point &a, const point &b) { return a.x == b.x && a.y == b.y; } int n, m, z, p; point a[N], p1[N], p2[N]; namespace DS1 { struct kd_tree { static const int N = 1e5 + 5; const double A = 0.7; #define ls tr[p].lc #define rs tr[p].rc #define mid (l + r >> 1) int rt, tot, cnt; using point = array<int, 2>; struct node { int lc, rc, siz, sum, w; point v, a, b; } tr[N]; void maintain(int p) { tr[p].siz = tr[ls].siz + tr[rs].siz + 1; tr[p].sum = tr[ls].sum + tr[rs].sum + tr[p].w; tr[p].a = tr[p].b = tr[p].v; if (ls) { tr[p].a[0] = min(tr[p].a[0], tr[ls].a[0]); tr[p].a[1] = min(tr[p].a[1], tr[ls].a[1]); tr[p].b[0] = max(tr[p].b[0], tr[ls].b[0]); tr[p].b[1] = max(tr[p].b[1], tr[ls].b[1]); } if (rs) { tr[p].a[0] = min(tr[p].a[0], tr[rs].a[0]); tr[p].a[1] = min(tr[p].a[1], tr[rs].a[1]); tr[p].b[0] = max(tr[p].b[0], tr[rs].b[0]); tr[p].b[1] = max(tr[p].b[1], tr[rs].b[1]); } } int g[N]; int rebuild(int l, int r, int k) { if (l > r) return 0; nth_element(g + l, g + mid, g + r + 1, [&](int x, int y) { return tr[x].v[k] < tr[y].v[k]; }); tr[g[mid]].lc = rebuild(l, mid - 1, k ^ 1); tr[g[mid]].rc = rebuild(mid + 1, r, k ^ 1); maintain(g[mid]); return g[mid]; } void dfs(int p) { if (p) g[++tot] = p, dfs(ls), dfs(rs); } void check(int &p, int k) { if (A * tr[p].siz < max(tr[ls].siz, tr[rs].siz)) tot = 0, dfs(p), p = rebuild(1, tot, k); } void newnode(int x, int y, int w) { ++cnt, tr[cnt].v = {x, y}, tr[cnt].w = w; } void insert(int &p, int k) { if (!p) { p = cnt; maintain(p); return; } if (tr[cnt].v[k] < tr[p].v[k]) insert(ls, k ^ 1); else insert(rs, k ^ 1); maintain(p), check(p, k); } int query(int p, point a, point b) { if (!p) return 0; int a0 = tr[p].a[0], a1 = tr[p].a[1], b0 = tr[p].b[0], b1 = tr[p].b[1]; if (b[0] < a0 || a[0] > b0 || b[1] < a1 || a[1] > b1) return 0; if (a[0] <= a0 && b0 <= b[0] && a[1] <= a1 && b1 <= b[1]) return tr[p].sum; int res = query(ls, a, b) + query(rs, a, b); int v0 = tr[p].v[0], v1 = tr[p].v[1], w = tr[p].w; if (a[0] <= v0 && v0 <= b[0] && a[1] <= v1 && v1 <= b[1]) res += w; return res; } void clear() { rt = cnt = 0; mem(tr, 0); } } tr; void update(int x, int y) { tr.newnode(x, y, 1), tr.insert(tr.rt, 0); } int query(int x0, int x1, int y0, int y1) { return tr.query(tr.rt, {x0, y0}, {x1, y1}); } void clear() { tr.clear(); } } namespace DS2 { // 求二维偏序 static const int N = 3.2e6 + 5; struct BIT { #define lb(i) (i&-i) int c[N]; void add(re int i, re int v) { for (; i < M; i += lb(i)) c[i] += v; } int query(re int i) { int r = 0; for (; i; i -= lb(i)) r += c[i]; return r; } void clear(int n) { for (re int i = 1; i <= n; i++) c[i] = 0; } } tr; int ori[N], tot, res[N]; int qu[N], cnt; struct node { bool type; // 0: update(x, y v); 1: query(1, x, y) int ori_id, x, y; friend bool operator==(const node &a, const node &b) { return a.type == b.type && a.x == b.x && a.y == b.y; } } dat[N]; void init_query(re int x0, re int x1, re int y0, re int y1, re int id) { if (x1 < x0 || y1 < y0) return; x0--, y0--; qu[cnt] = id; dat[++cnt] = {1, cnt, x1, y1}; dat[++cnt] = {1, cnt, x0, y1}; dat[++cnt] = {1, cnt, x1, y0}; dat[++cnt] = {1, cnt, x0, y0}; ori[++tot] = y0; ori[++tot] = y1; } void init_update(re int x, re int y) { dat[++cnt] = {0, cnt, x, y}; ori[++tot] = y; } int gethash(re int y) { int l = 1, r = tot; while (l <= r) { if (ori[mid] == y) return mid; else if (y < ori[mid]) r = mid - 1; else l = mid + 1; } assert(0); } void init_hash() { sort(ori + 1, ori + tot + 1); tot = unique(ori + 1, ori + tot + 1) - ori - 1; } void solve(int *ans, int *ans2) { init_hash(); sort(dat + 1, dat + cnt + 1, [](const node &x, const node &y) { if (x.x != y.x) return x.x < y.x; return x.type < y.type; }); for (re int i = 1; i <= cnt; i++) { auto [type, ori_id, x, y] = dat[i]; if (type == 0) tr.add(gethash(y), 1); else res[ori_id] = tr.query(gethash(y)); } for (re int i = 0; i + 4 <= cnt; i++) { re int j = qu[i], t = res[i + 1] - res[i + 2] - res[i + 3] + res[i + 4]; if (j) { if (j > p) ans[j - p] += t, ans2[j - p] += t; else if (j > 0) ans[j] += t; else ans2[-j] += t; } } tr.clear(tot); } void clear() { cnt = tot = 0; mem(qu, 0); } } struct node { bool type; // 0: add(x0, y0, xy), 1: query(x0, x1, y0, y1, xy) int id; int x0, x1, y0, y1, xy; friend bool operator==(const node &x, const node &y) { return x.type == y.type && x.x0 == y.x0 && x.x1 == y.x1 && x.y0 == y.y0 && x.y1 == y.y1; } } dat[M]; int tot, vis[N]; // vis[i]: 第i个询问有没有处理 #define Le(x) ((int)floor(x)) #define Lt(x) ((int)ceil(x) - 1) inline int le(int x, int y) { return x - (y + 1 >> 1); } // Le(x - y / 2) = floor(x - y / 2) inline int lt(int x, int y) { return x - (y >> 1) - 1; } // Lt(x - y / 2) = ceil(x - y / 2) - 1 void solve(int *ans, int *ans2) { DS1::clear(), DS2::clear(); tot = 0; for (re int i = 1; i <= p; i++) if (!vis[i]) { if (p1[i] == p2[i]) { vis[i] = 1; DS2::init_query(1, n, 1, m, -i); } else if (p1[i].x <= p2[i].x && p1[i].y <= p2[i].y) { vis[i] = 1; re int a = p2[i].x - p1[i].x, b = p2[i].y - p1[i].y, c, d; // 1 DS2::init_query(1, p1[i].x - 1, 1, p1[i].y - 1, i + p); // 2 c = min(lt(p2[i].x, a - b), p2[i].x), d = min(le(p2[i].x, a - b), p2[i].x); if (d >= p1[i].x) { if (c == d) DS2::init_query(p1[i].x, c, 1, p1[i].y - 1, i + p); else DS2::init_query(p1[i].x, c, 1, p1[i].y - 1, i), DS2::init_query(p1[i].x, d, 1, p1[i].y - 1, -i); } // 3 if (a < b) DS2::init_query(p2[i].x + 1, n, 1, p1[i].y - 1, i + p); else if (a == b) DS2::init_query(p2[i].x + 1, n, 1, p1[i].y - 1, -i); // 4 c = min(lt(p2[i].y, b - a), p2[i].y), d = min(le(p2[i].y, b - a), p2[i].y); if (d >= p1[i].y) { if (c == d) DS2::init_query(1, p1[i].x - 1, p1[i].y, c, i + p); else DS2::init_query(1, p1[i].x - 1, p1[i].y, c, i), DS2::init_query(1, p1[i].x - 1, p1[i].y, d, -i); } // 5 c = lt(p2[i].x + p2[i].y, a + b), d = le(p2[i].x + p2[i].y, a + b); if (c == d) dat[++tot] = {1, i + p, p1[i].x, p2[i].x, p1[i].y, p2[i].y, c}; else dat[++tot] = {1, i, p1[i].x, p2[i].x, p1[i].y, p2[i].y, c}, dat[++tot] = {1, -i, p1[i].x, p2[i].x, p1[i].y, p2[i].y, d}; // 6 c = min(lt(p2[i].y, b + a), p2[i].y), d = min(le(p2[i].y, b + a), p2[i].y); if (d >= p1[i].y) { if (c == d) DS2::init_query(p2[i].x + 1, n, p1[i].y, c, i + p); else DS2::init_query(p2[i].x + 1, n, p1[i].y, c, i), DS2::init_query(p2[i].x + 1, n, p1[i].y, d, -i); } // 7 if (b < a) DS2::init_query(1, p1[i].x - 1, p2[i].y + 1, m, i + p); else if (b == a) DS2::init_query(1, p1[i].x - 1, p2[i].y + 1, m, -i); // 8 c = min(lt(p2[i].x, a + b), p2[i].x), d = min(le(p2[i].x, a + b), p2[i].x); if (d >= p1[i].x) { if (c == d) DS2::init_query(p1[i].x, c, p2[i].y + 1, m, i + p); else DS2::init_query(p1[i].x, c, p2[i].y + 1, m, i), DS2::init_query(p1[i].x, d, p2[i].y + 1, m, -i); } } } for (re int i = 1; i <= z; i++) DS2::init_update(a[i].x, a[i].y); for (re int i = 1; i <= z; i++) dat[++tot] = {0, -1, a[i].x, 0, a[i].y, 0, a[i].x + a[i].y}; sort(dat + 1, dat + tot + 1, [](const node &a, const node &b) { return a.xy == b.xy ? a.type < b.type : a.xy < b.xy; }); for (re int i = 1, t; i <= tot; i++) { auto [type, id, x0, x1, y0, y1, xy] = dat[i]; if (type == 0) DS1::update(x0, y0); else { if (!(dat[i] == dat[i - 1])) t = DS1::query(x0, x1, y0, y1); if (id > p) ans[id - p] += t, ans2[id - p] += t; else if (id > 0) ans[id] += t; else ans2[-id] += t; } } DS2::solve(ans, ans2); } #define opx(x) (n + 1 - (x)) #define opy(y) (m + 1 - (y)) int ans[N], ans2[N]; // dist1<dsti2 / dist1<=dist2 void get_ans(int *ans, int *ans2) { solve(ans, ans2); for (re int i = 1; i <= z; i++) a[i].y = opy(a[i].y); for (re int i = 1; i <= p; i++) p1[i].y = opy(p1[i].y), p2[i].y = opy(p2[i].y); solve(ans, ans2); } int swp[N]; signed main() { using fastio::read, fastio::write; read(n), read(m), read(z), read(p); for (re int i = 1; i <= z; i++) read(a[i].x), read(a[i].y); for (re int i = 1; i <= p; i++) read(p1[i].x), read(p1[i].y), read(p2[i].x), read(p2[i].y); for (re int i = 1; i <= p; i++) if (p1[i].x > p2[i].x) swap(p1[i], p2[i]), swp[i] = 1; get_ans(ans, ans2); for (re int i = 1; i <= p; i++) { int a = ans[i], b = z - ans2[i], c = ans2[i] - ans[i]; if (swp[i]) swap(a, b); write(a), putchar(' '); write(b), putchar(' '); write(c), putchar('\n'); } return 0; }
- 1
信息
- ID
- 2773
- 时间
- 6500ms
- 内存
- 256MiB
- 难度
- 9
- 标签
- 递交数
- 58
- 已通过
- 3
- 上传者