P9062 [Ynoi2002] Adaptive Hsearch&Lsearch 题解

Description

nn 个点 p1,p2,,pnp_1,p_2,\dots,p_n 在二维平面上。

qq 次询问,在第 ii 个询问中,给定两个数 li,ril_i,r_i (1li<rin1\leq l_i< r_i\leq n),你需要找到一对 (u,v)(u,v) 满足 liu<vril_i\leq u<v\leq r_ipup_upvp_v 之间的欧几里得距离 (xuxv)2+(yuyv)2\sqrt{(x_u-x_v)^2+(y_u-y_v)^2} 最小。

n,q2.5×105n,q\leq 2.5\times 10^5

Solution

平面最近点对有一个做法是分别按照 d0×d0,d1×d1,,dc×dcd^0\times d^0,d^1\times d^1,\ldots,d^c\times d^c 对二维平面进行分块,每次新加入一个点 ii,对所有 0kc0\leq k\leq c,选择所有 ii 所在块周围的 3×33\times 3 个相邻块中的点更新。

更新后把这些相邻块中与 ii 距离小于 dk1d^{k-1} 的点删掉。

这个做法保证了每个对于所有 kk,每个块中的点数都是一个比较小的量级。正确性就考虑设 x<y<zx<y<z,假设 (x,z)(x,z) 对答案有贡献,就说明所有 y[x+1,z1]y\in[x+1,z-1],都满足 dis(x,y)dis(x,z)dis(x,y)\geq dis(x,z)

但是有可能对于一个 kk,如果 dis(x,z)dis(x,y)<dk1dis(x,z)\leq dis(x,y)<d^{k-1},此时 xx 会在 yy 加入后被删掉,在 zz 插入时就找不到 xx 了。

对于这种情况,可以发现将 kk 缩小到 dis(x,y)dk1dis(x,y)\geq d^{k-1}xx 就不会被删掉了,由于这些 disdis 都大于等于 11,所以一定能找到这样的 kk,也就说明每个可能的有效点对都会被计算到。

容易发现总共有 O(nlogV)O(n\log V) 个点对,用分块+扫描线维护答案即可。

时间复杂度:O(nlogV+qn)O(n\log V+q\sqrt n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>

// #define int int64_t

using i64 = int64_t;

struct custom_hash {
static uint64_t splitmix64(uint64_t x) {
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}
size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM = std::chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x + FIXED_RANDOM);
}
size_t operator()(std::pair<uint64_t, uint64_t> x) const {
static const uint64_t FIXED_RANDOM = std::chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x.first + FIXED_RANDOM) ^
(splitmix64(x.second + FIXED_RANDOM) >> 1);
}
size_t operator()(std::tuple<uint64_t, uint64_t, uint64_t> x) const {
static const uint64_t FIXED_RANDOM = std::chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(std::get<0>(x) + FIXED_RANDOM) ^
(splitmix64(std::get<1>(x) + FIXED_RANDOM) >> 1) ^
(splitmix64(std::get<2>(x) + FIXED_RANDOM) >> 2);
}
};

const int kMaxN = 2.5e5 + 5, kMaxB = 505;

int n, q;
int x[kMaxN], y[kMaxN];
i64 ans[kMaxN];
std::vector<int> vv[kMaxN];
std::vector<std::pair<int, int>> qq[kMaxN];

void chkmin(i64 &x, i64 y) { x = (x < y ? x : y); }

struct Block {
int b, tot, bel[kMaxN], L[kMaxB], R[kMaxB];
i64 mi1[kMaxB], mi2[kMaxN];
void init(int n) {
b = sqrtl(n);
if (!b) ++b;
tot = (n + b - 1) / b;
for (int i = 1; i <= tot; ++i) {
L[i] = (i - 1) * b + 1, R[i] = std::min(i * b, n);
mi1[i] = 1e18;
for (int j = L[i]; j <= R[i]; ++j)
bel[j] = i, mi2[j] = 1e18;
}
}
void update(int x, i64 v) {
chkmin(mi1[bel[x]], v), chkmin(mi2[x], v);
}
i64 query(int x) {
i64 ret = 1e18;
for (int i = bel[x] + 1; i <= tot; ++i) chkmin(ret, mi1[i]);
for (int i = x; i <= R[bel[x]]; ++i) chkmin(ret, mi2[i]);
return ret;
}
} t;

i64 getdis(int i, int j) {
return (i64)(x[i] - x[j]) * (x[i] - x[j]) + (i64)(y[i] - y[j]) * (y[i] - y[j]);
}

void getans() {
t.init(n);
for (int i = 1; i <= n; ++i) {
for (auto j : vv[i]) t.update(j, getdis(i, j));
for (auto [j, id] : qq[i]) ans[id] = t.query(j);
}
}

void dickdreamer() {
std::cin >> n >> q;
for (int i = 1; i <= n; ++i) std::cin >> x[i] >> y[i];
for (int k = 0, d = 4, pw = 1; pw <= 2e8; ++k, pw *= d) {
__gnu_pbds::gp_hash_table<std::pair<int, int>, std::vector<int>, custom_hash> mp;
for (int i = 1; i <= n; ++i) {
int bx = x[i] / pw, by = y[i] / pw;
for (int px = bx - 1; px <= bx + 1; ++px) {
for (int py = by - 1; py <= by + 1; ++py) {
if (px < 0 || py < 0) continue;
if (mp.find({px, py}) == mp.end()) continue;
auto &vec = mp[{px, py}];
std::vector<int> dv;
for (auto j : vec) {
vv[i].emplace_back(j);
if (getdis(i, j) >= std::max<i64>(1, 1ll * pw * pw / d / d)) dv.emplace_back(j);
}
vec = dv;
}
}
mp[{bx, by}].emplace_back(i);
}
}
for (int i = 1; i <= q; ++i) {
int l, r;
std::cin >> l >> r;
qq[r].emplace_back(l, i);
}
getans();
for (int i = 1; i <= q; ++i) std::cout << ans[i] << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P9062 [Ynoi2002] Adaptive Hsearch&amp;Lsearch 题解
https://sobaliuziao.github.io/2025/04/13/post/2c743f77.html
作者
Egg_laying_master
发布于
2025年4月13日
许可协议