• 【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;
    }
    
  • 相关阅读:
    【UOJ Round #3】
    【UOJ Round #1】
    【BZOJ】【3673】可持久化并查集 & 【3674】可持久化并查集加强版
    【ContestHunter】【弱省胡策】【Round7】
    【BZOJ】【3211】花神游历各国
    【BZOJ】【4146】 【AMPPZ2014】Divisors
    【BZOJ】【3931】【CQOI2015】网络吞吐量
    【BZOJ】【3697】采药人的路径&【3127】【USACO2013 Open】Yin and Yang
    【BZOJ】【3930】【CQOI2015】选数
    【ContestHunter】【弱省胡策】【Round6】
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_4-2-3.html
Copyright © 2020-2023  润新知