初始有一些点,分布在二维平面上。
然后要你进行一些操作:再往上面放一个点,或者给你一个坐标,找到距离它最近的点到它的距离。
(这里的距离是哈密顿距离,就 x,y 坐标差的和)
[Violet]天使玩偶 / SJY摆棋子
题目大意
初始有一些点,分布在二维平面上。
然后要你进行一些操作:再往上面放一个点,或者给你一个坐标,找到距离它最近的点到它的距离。
(这里的距离是哈密顿距离,就 x,y 坐标差的和)
思路
这道题我们用 K-D tree 来做。
(它其实可以说是 K-D tree 的模板题?)
那就来略讲一下 K-D tree。
K-D tree 就是一个可以存 k 维的点一个二叉树。
然后你这个 K-D tree 就相当于对这些点构成的 k 维空间的一个划分,树中每个点代表一块空间。
而这道题中我们用的是二维 K-D tree,一般用到的也是这个。
它大概是这样,每个深度有一个划分的维度 d,然后在这个深度的点中,它的左儿子的 d 维信息小于它的,它右儿子的 d 维信息大于它的。
那很容易看出你划分的维度当然是轮流来的,就比如先第一维,接着第二维,然后第三维,再变成第一维,以此类推。
(不过说一开始轮的顺序也可以按方差从大到小排,可以缩小时间)
然后划分的依据,容易想到我们要让树尽可能平衡,那我们构树的时候就选中位数作为代表的点,然后再把其他点分到左右两边。(可以用 nth-element)
然后容易看出它是可以不断新放别的点的,就按照每个点,从根节点一直往下走,走到没有点的地方就放在那里。
但是你会想到放多了它可能就不平衡了,那就用替罪羊的思想,也搞个失衡的值。
如果两个子树有一个的个数超过它的个数乘上这个值,就把这个树拍扁重构。
那搞到这一题大概就是你每个点代表一个矩形,而且每个点在树上都有一个代表的点。
然后对于这个节点代表的点,我们求出它到询问给出点的距离。
然后再搞出它两个儿子代表的矩阵到询问点的最短距离。
那这个距离就是这个矩阵里面的点到这个询问点距离的下界,那如果这个下界都比你当前的答案大,那就没有必要跑这个矩阵里面的点了。
(算点到矩阵距离大概就是看四条边,点到四条边的距离)
然后还有一个优化就是,我们可以先跑下界小的矩阵,再跑下界大的。
因为这样答案会更新的更小一点,就可以跳过更多的矩阵。
然后大概这样就好了。
(主要是实现烦)
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#define INF 1e9
#define alph (0.7)
#define rr register
using namespace std;
struct zb {
int w[2];
}a[1000001], tmp;
struct node {
zb a;
int l, r, ma[2], mi[2], size;
}tree[1000001];
int n, m, op, root, tot, ans;
int rebuild[1000001], WD;
int read() {
int re = 0;
char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
re = (re << 3) + (re << 1) + c - '0';
c = getchar();
}
return re;
}
void write(rr int now) {
if (now > 9) write(now / 10);
putchar(now % 10 + '0');
}
int operator <(zb x, zb y) {
return x.w[WD] < y.w[WD];
}
int get_point() {
if (rebuild[0]) return rebuild[rebuild[0]--];
return ++tot;
}
int abss(rr int now) {
if (now < 0) return -now;
return now;
}
int up(rr int now) {
for (int i = 0; i < 2; i++) {
tree[now].mi[i] = tree[now].a.w[i];
tree[now].ma[i] = tree[now].a.w[i];
if (tree[now].l) {
tree[now].mi[i] = min(tree[now].mi[i], tree[tree[now].l].mi[i]);
tree[now].ma[i] = max(tree[now].ma[i], tree[tree[now].l].ma[i]);
}
if (tree[now].r) {
tree[now].mi[i] = min(tree[now].mi[i], tree[tree[now].r].mi[i]);
tree[now].ma[i] = max(tree[now].ma[i], tree[tree[now].r].ma[i]);
}
}
tree[now].size = tree[tree[now].l].size + tree[tree[now].r].size + 1;
}
int build(rr int l, rr int r, rr int wd) {
if (l > r) return 0;
rr int now = get_point();
rr int mid = (l + r) >> 1;
WD = wd;
nth_element(a + l, a + mid, a + r + 1);
tree[now].a = a[mid];
tree[now].l = build(l, mid - 1, wd ^ 1);
tree[now].r = build(mid + 1, r, wd ^ 1);
up(now);
return now;
}
void make_again(rr int root, rr int num) {
if (tree[root].l) make_again(tree[root].l, num);
a[num + tree[tree[root].l].size + 1] = tree[root].a;
rebuild[++rebuild[0]] = root;
if (tree[root].r) make_again(tree[root].r, num + tree[tree[root].l].size + 1);
}
void check(rr int &root, rr int wd) {
if (alph * tree[root].size < tree[tree[root].l].size || alph * tree[root].size < tree[tree[root].r].size) {
make_again(root, 0);
root = build(1, tree[root].size, wd);
}
}
void insert(rr zb now, rr int &root, rr int wd) {
if (!root) {
root = get_point();
tree[root].a = now;
tree[root].l = tree[root].r = 0;
up(root);
return ;
}
if (tree[root].a.w[wd] < now.w[wd])
insert(now, tree[root].r, wd ^ 1);
else insert(now, tree[root].l, wd ^ 1);
up(root);
check(root, wd);
}
int get_dis(rr zb x, rr zb y) {
return abss(x.w[0] - y.w[0]) + abss(x.w[1] - y.w[1]);
}
int blog_dis(rr zb x, rr int now) {
rr int re = 0;
for (int i = 0; i < 2; i++) {
re += max(0, tree[now].mi[i] - x.w[i]);
re += max(0, x.w[i] - tree[now].ma[i]);
}
return re;
}
void get_close(rr zb now, rr int root) {
ans = min(ans, get_dis(now, tree[root].a));
rr int ldis = INF, rdis = INF;
if (tree[root].l) ldis = blog_dis(now, tree[root].l);
if (tree[root].r) rdis = blog_dis(now, tree[root].r);
if (ldis < rdis) {
if (ldis < ans) get_close(now, tree[root].l);
if (rdis < ans) get_close(now, tree[root].r);
}
else {
if (rdis < ans) get_close(now, tree[root].r);
if (ldis < ans) get_close(now, tree[root].l);
}
}
int main() {
n = read();
m = read();
for (rr int i = 1; i <= n; i++) {
a[i].w[0] = read();
a[i].w[1] = read();
}
root = build(1, n, 0);
while (m--) {
op = read();
tmp.w[0] = read();
tmp.w[1] = read();
if (op == 1) {
insert(tmp, root, 0);
}
else {
ans = INF;
get_close(tmp, root);
write(ans);
putchar('\n');
}
}
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现