数据结构 Trick 之:KDT 求 k 近/远 点
注意,此 Trick 的时间复杂度是错的,但是貌似目前没人能卡满。
能够解决的问题
- \(O(n \sqrt n)\) 可过。
- 维护二维平面。
- 每次求到一个点的 \(k\) 近或 \(k\) 远点。
- \(k\) 很小(\(20\) 左右)
思路
二维空间想到 KDTree(TreeKevin Durant Tree)。
众所周知,动态维护 \(k\) 近或 \(k\) 远点只需要一个 \(k\) 个点的堆即可,那么我们就有了一个剪枝:当前节点维护的区间到询问点的理论极值(写个估价函数就行)都比不上当前 \(k\) 大/小值,就返回。
结束了,就是这么简单。
例题与代码
#include <bits/stdc++.h>
using namespace std;
namespace gxk {
void main() ;
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
gxk::main();
return 0;
}
// ---
#define int long long
namespace gxk {
constexpr int maxn = 100010;
struct point {
int x[2];
} p;
int n, m, x, b[maxn];
struct value {
int dis, idx;
bool operator < (const value o) const {
return dis > o.dis || (dis == o.dis && idx < o.idx);
}
} ;
priority_queue <value> q;
struct Kevin_DurantNode {
point x, l, r;
int ls, rs, idx;
} nodes[maxn];
int cnt;
struct Kevin_DurantTree {
void pushup(int u) {
for (int k = 0; k < 2; k++) {
nodes[u].l.x[k] = nodes[u].r.x[k] = nodes[u].x.x[k];
if (nodes[u].ls) {
nodes[u].l.x[k] = min(nodes[u].l.x[k], nodes[nodes[u].ls].l.x[k]);
nodes[u].r.x[k] = max(nodes[u].r.x[k], nodes[nodes[u].ls].r.x[k]);
}
if (nodes[u].rs) {
nodes[u].l.x[k] = min(nodes[u].l.x[k], nodes[nodes[u].rs].l.x[k]);
nodes[u].r.x[k] = max(nodes[u].r.x[k], nodes[nodes[u].rs].r.x[k]);
}
}
return ;
}
void build(int &u, int l, int r, int k) {
int midd = ((l + r) >> 1);
nth_element(b + l, b + midd, b + r + 1, [k](int a, int b){return nodes[a].x.x[k] < nodes[b].x.x[k];});
u = b[midd];
// cout << nodes[u].x.x[0] << ' ' << nodes[u].x.x[1] << '\n';
if (l < midd) build(nodes[u].ls, l, midd - 1, k ^ 1);
if (r > midd) build(nodes[u].rs, midd + 1, r, k ^ 1);
pushup(u);
return ;
}
int dis(int x, int y) {
return (x - p.x[0]) * (x - p.x[0]) + (y - p.x[1]) * (y - p.x[1]);
}
int evaluate(int u) {
return max({
dis(nodes[u].l.x[0], nodes[u].l.x[1]),
dis(nodes[u].l.x[0], nodes[u].r.x[1]),
dis(nodes[u].r.x[0], nodes[u].l.x[1]),
dis(nodes[u].r.x[0], nodes[u].r.x[1])});
}
void query(int u) {
value now = {evaluate(u), nodes[u].idx};
if (!(now < q.top())) return ;
now = {dis(nodes[u].x.x[0], nodes[u].x.x[1]), nodes[u].idx};
if (now < q.top()) q.pop(), q.push(now);
if (nodes[u].ls && nodes[u].rs) {
value vl = {evaluate(nodes[u].ls), nodes[nodes[u].ls].idx}, vr = {evaluate(nodes[u].rs), nodes[nodes[u].rs].idx};
if (vl < vr) {
query(nodes[u].ls);
query(nodes[u].rs);
} else {
query(nodes[u].rs);
query(nodes[u].ls);
}
} else if (nodes[u].ls) {
query(nodes[u].ls);
} else if (nodes[u].rs) {
query(nodes[u].rs);
}
return ;
}
} kdt;
int root;
void main() {
cin >> n;
cnt = n;
for (int i = 1; i <= n; i++) {
cin >> nodes[i].x.x[0] >> nodes[i].x.x[1];
nodes[i].idx = i;
b[i] = i;
}
kdt.build(root, 1, n, 0);
cin >> m;
while (m--) {
cin >> p.x[0] >> p.x[1] >> x;
while (!q.empty()) q.pop();
while (x--) {
q.push({-1, 0});
}
kdt.query(root);
cout << q.top().idx << '\n';
}
return ;
}
}