【bzoj4520】[Cqoi2016]K远点对 KD-tree+堆
题目描述
已知平面内 N 个点的坐标,求欧氏距离下的第 K 远点对。
输入
输入文件第一行为用空格隔开的两个整数 N, K。接下来 N 行,每行两个整数 X,Y,表示一个点
的坐标。1 < = N < = 100000, 1 < = K < = 100, K < = N*(N−1)/2 , 0 < = X, Y < 2^31。
输出
输出文件第一行为一个整数,表示第 K 远点对的距离的平方(一定是个整数)。
样例输入
10 5
0 0
0 1
1 0
1 1
2 0
2 1
1 2
0 2
3 0
3 1
样例输出
9
题解
KD-tree+堆
考虑求与一个点距离第k大的点怎么求:维护一个有k个元素的小根堆,表示距离,初始时都是极小值;每次扫到一个点,如果距离大于堆顶,那么就把堆顶元素换成这个距离。并根据左右子树的估价函数与堆顶的关系决定是否向下查询。最后堆顶就是答案。
那么换成所有点之间的呢?直接维护同一个堆,对所有点求一次就好了。但是要注意,由于一对点会被计算两次,所以要维护2k个元素。
#include <queue> #include <cstdio> #include <cstring> #include <algorithm> #define N 100010 #define squ(x) (ll)(x) * (x) using namespace std; typedef long long ll; priority_queue<ll> q; int d , root; struct data { int p[2] , mx[2] , mn[2] , c[2]; bool operator<(data a)const {return p[d] == a.p[d] ? p[d ^ 1] < a.p[d ^ 1] : p[d] < a.p[d];} }a[N]; void pushup(int k , int x) { a[k].mx[0] = max(a[k].mx[0] , a[x].mx[0]); a[k].mn[0] = min(a[k].mn[0] , a[x].mn[0]); a[k].mx[1] = max(a[k].mx[1] , a[x].mx[1]); a[k].mn[1] = min(a[k].mn[1] , a[x].mn[1]); } int build(int l , int r , int now) { int mid = (l + r) >> 1; d = now , nth_element(a + l , a + mid , a + r + 1); a[mid].mx[0] = a[mid].mn[0] = a[mid].p[0]; a[mid].mx[1] = a[mid].mn[1] = a[mid].p[1]; if(l < mid) a[mid].c[0] = build(l , mid - 1 , now ^ 1) , pushup(mid , a[mid].c[0]); if(r > mid) a[mid].c[1] = build(mid + 1 , r , now ^ 1) , pushup(mid , a[mid].c[1]); return mid; } ll getdis(int k , int x , int y) { return max(squ(a[k].mx[0] - x) , squ(a[k].mn[0] - x)) + max(squ(a[k].mx[1] - y) , squ(a[k].mn[1] - y)); } void query(int k , int x , int y) { ll dn = squ(a[k].p[0] - x) + squ(a[k].p[1] - y) , dl = (a[k].c[0] ? getdis(a[k].c[0] , x , y) : -1ll << 62) , dr = (a[k].c[1] ? getdis(a[k].c[1] , x , y) : -1ll << 62); if(dn > -q.top()) q.pop() , q.push(-dn); if(dl > dr) { if(dl > -q.top()) query(a[k].c[0] , x , y); if(dr > -q.top()) query(a[k].c[1] , x , y); } else { if(dr > -q.top()) query(a[k].c[1] , x , y); if(dl > -q.top()) query(a[k].c[0] , x , y); } } int main() { int n , k , i; scanf("%d%d" , &n , &k); for(i = 1 ; i <= n ; i ++ ) scanf("%d%d" , &a[i].p[0] , &a[i].p[1]); root = build(1 , n , 0); for(i = 1 ; i <= 2 * k ; i ++ ) q.push(0); for(i = 1 ; i <= n ; i ++ ) query(root , a[i].p[0] , a[i].p[1]); printf("%lld\n" , -q.top()); return 0; }