【ybt金牌导航4-2-3】【luogu P4357】K远点对
K远点对
题目链接:ybt金牌导航4-2-3 / luogu P4357
题目大意
一个平面上有一些点,你要找到第 K 远对点之间的距离。
距离是用勾股的那个算。
思路
那你看到平面距离自然想到 K-D tree。
那你看到它要所有两对点,那你就把每两个点之间的距离都放进去比较。
然后你就想到先把每个点丢进 K-D tree 里面,然后再用所有的点轮流去查询,然后把所有的距离用堆维护出前 \(k\) 大。
然后你会发现 \(a\) 到 \(b\) 和 \(b\) 到 \(a\) 你是比较了两次的,但它其实只算一次。
(你 \(a\) 会查询到 \(b\),\(b\) 会查询到 \(a\))
那也就是说,两个点之间的距离会被放进去两次,那你很容易想到,你就直接搞一个 \(2k\) 的堆,然后最后的堆顶就是我们的第 \(k\) 大了。
代码
#include<queue>
#include<cstdio>
#include<algorithm>
#define ll long long
#define INF 10000000
using namespace std;
struct zb {
int w[2];
}a[200001];
struct KDtree {
int l, r, ma[2], mi[2];
}tree[200001];
int n, k, root, WD, num[200001];
priority_queue <ll, vector<ll>, greater<ll> > q;
bool cmp(int x, int y) {
return a[x].w[WD] < a[y].w[WD];
}
void up(int now) {
for (int i = 0; i < 2; i++) {
tree[now].ma[i] = tree[now].mi[i] = a[now].w[i];
if (tree[now].l) {
tree[now].ma[i] = max(tree[now].ma[i], tree[tree[now].l].ma[i]);
tree[now].mi[i] = min(tree[now].mi[i], tree[tree[now].l].mi[i]);
}
if (tree[now].r) {
tree[now].ma[i] = max(tree[now].ma[i], tree[tree[now].r].ma[i]);
tree[now].mi[i] = min(tree[now].mi[i], tree[tree[now].r].mi[i]);
}
}
}
int build(int l, int r, int wd) {
if (l > r) return 0;
int mid = (l + r) >> 1;
WD = wd;
nth_element(num + l, num + mid, num + r + 1, cmp);
int now = num[mid];
tree[now].l = build(l, mid - 1, wd ^ 1);
tree[now].r = build(mid + 1, r, wd ^ 1);
up(now);
return now;
}
ll get_dis(zb x, zb y) {
ll X = x.w[0] - y.w[0];
ll Y = x.w[1] - y.w[1];
return X * X + Y * Y;
}
ll abss(ll x) {
if (x < 0) return -x;
return x;
}
ll blog_dis(zb x, KDtree y) {
ll X = max(abss(x.w[0] - y.ma[0]), abss(x.w[0] - y.mi[0]));
ll Y = max(abss(x.w[1] - y.ma[1]), abss(x.w[1] - y.mi[1]));
return X * X + Y * Y;
}
void query(zb now, int root) {
if (!root) return ;
ll diss = get_dis(now, a[root]);
if (diss > q.top()) {
q.pop();
q.push(diss);
}
ll ldis = -INF;
ll rdis = -INF;
if (tree[root].l) ldis = blog_dis(now, tree[tree[root].l]);
if (tree[root].r) rdis = blog_dis(now, tree[tree[root].r]);
if (ldis > rdis) {
if (ldis > q.top()) query(now, tree[root].l);
if (rdis > q.top()) query(now, tree[root].r);
}
else {
if (rdis > q.top()) query(now, tree[root].r);
if (ldis > q.top()) query(now, tree[root].l);
}
}
int main() {
scanf("%d %d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d %d", &a[i].w[0], &a[i].w[1]);
num[i] = i;
}
root = build(1, n, 0);
k <<= 1;//由于 a 到 b 和 b 到 a 两个是一样的但算了两次,所以我们索性就求出你这些所有距离的第 2k 大
for (int i = 1; i <= k; i++) q.push(-1);
for (int i = 1; i <= n; i++) {//把每个点和其他点之间的距离都比较一次,维护前 2k 大
query(a[i], root);
}
printf("%lld", q.top());
return 0;
}