查找k远点 / JZPFAR
题目链接:ybt金牌导航4-2-2 / luogu P2093
题目大意
一个图上有一些点,然后多次询问。
每次询问给你一个位置,要你找距离这个位置第 k 远的点的编号。
如果两个点到它的距离都一样,编号小的距离大。
思路
首先看到二维平面找距离相关的,可以想到用 K-D tree。
但是它不是要最远,而是要第
k
k
k 远。
但你发现
k
k
k 很小,最大
20
20
20,那你考虑把前
k
k
k 大都维护出来。
拿一个堆即可解决。
(堆顶是当前第
k
k
k 大,如果你要判断的比它大,就把它拿出来,你的放进去)
(这个堆一开始就放
k
k
k 个
−
1
-1
−1,保证点要判断就一定可以)
然后记得这里的距离是勾股算的那个,为了避免精度问题我们就可以不开根号。(因为我输出的只是编号)
然后由于你不开根号,所以结果要用 long long 来存。
而且!算的过程中一定要先转成 long long 再乘。
(我就这样 WA 爆蛋了一页的评测)
代码
#include<queue>
#include<cstdio>
#include<algorithm>
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
using namespace std;
struct zb {
int w[2];
}a[500001], tmp;
struct KDtree {
int ma[2], mi[2], l, r;
}tree[500001];
int n, root, WD, tot, m, x, y, rnk, num[500001];
priority_queue <pair<ll, int>, vector<pair<ll, int> >, greater<pair<ll, int> > > q;
void up(int now) {
for (int wd = 0; wd < 2; wd++) {
tree[now].ma[wd] = tree[now].mi[wd] = a[now].w[wd];
if (tree[now].l) {
tree[now].ma[wd] = max(tree[now].ma[wd], tree[tree[now].l].ma[wd]);
tree[now].mi[wd] = min(tree[now].mi[wd], tree[tree[now].l].mi[wd]);
}
if (tree[now].r) {
tree[now].ma[wd] = max(tree[now].ma[wd], tree[tree[now].r].ma[wd]);
tree[now].mi[wd] = min(tree[now].mi[wd], tree[tree[now].r].mi[wd]);
}
}
}
bool cmp(int x, int y) {
return a[x].w[WD] < a[y].w[WD];
}
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 getdis(zb x, zb y) {//点之间距离
ll X = x.w[0] - y.w[0], 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(KDtree x, zb y) {//点到矩阵距离
ll X = max(abss(x.ma[0] - y.w[0]), abss(x.mi[0] - y.w[0]));
ll Y = max(abss(x.ma[1] - y.w[1]), abss(x.mi[1] - y.w[1]));
return X * X + Y * Y;
}
void query(zb now, int root) {
if (!root) return ;
//第当前的第 k 大还大就说明要替换,直接弹出原来的把这个丢进去
if (q.top() < make_pair(getdis(now, a[root]), -root)) {
q.pop();
q.push(make_pair(getdis(now, a[root]), -root));
}
ll ldis = -INF, rdis = -INF;
if (tree[root].l)
ldis = blog_dis(tree[tree[root].l], now);
if (tree[root].r)
rdis = blog_dis(tree[tree[root].r], now);
if (ldis > rdis) {
if (ldis >= q.top().first) query(now, tree[root].l);
if (rdis >= q.top().first) query(now, tree[root].r);
}
else {
if (rdis >= q.top().first) query(now, tree[root].r);
if (ldis >= q.top().first) query(now, tree[root].l);
}
}
int main() {
scanf("%d", &n);
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);
scanf("%d", &m);
while (m--) {
scanf("%d %d %d", &tmp.w[0], &tmp.w[1], &rnk);
while (!q.empty()) q.pop();
for (int i = 1; i <= rnk; i++)//一开始插入 rnk 个 -1,这样一旦有结果出来就一定会换
q.push(make_pair(-1, 1));
query(tmp, root);
printf("%d\n", -q.top().second);
}
return 0;
}