P1429 平面最近点对(加强版)

知识点: 分治

原题面:Luogu


?随机旋转一个角度是个什么玩意啊


题意简述

给定 \(n\) 个二维平面上的坐标,求最小的欧几里得距离。
\(1\le n\le 2\times 10^5\)


分析题意

数据范围不是那么喜人,一个 \(\log\) 可过,考虑先固定一维,按横坐标排序后分治。
对于一个区间 \([l,r]\) 内的点对,递归处理左右半边内部的点对,仅考虑跨区间点对的贡献。
将当前统计到的,最小的欧几里得距离 记为 \(ans\),设为全局变量。

考虑跨分界线的点对的贡献。
显然距离不小于 \(ans\) 的点对,必然是没有贡献的。
距离 \(<ans\) 的点对,其横坐标之差一定 \(<ans\)
又已按照横坐标进行了排序,考虑找到 最远的 与分界线横坐标差 \(<ans\) 的 跨区间的点对,它们及夹在它们之间的点对,都有可能对答案有贡献。
这样的点对可以简单二分获得,统计跨分界线的点对的贡献时,仅需统计它们两两之间的距离即可。

复杂度比较玄学,但是跑得飞快。


代码实现

#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstring>
#define ll long long
const int kMaxn = 2e5 + 10;
const double kInf = 1e9 + 2077;
//=============================================================
struct Node {
  double x, y;
  bool operator < (const Node &sec_) const {
    return x < sec_.x;
  }
} node[kMaxn];
int n, llast, rlast;
double ans = kInf;
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void Getmin(double &fir_, double sec_) {
  if (sec_ < fir_) fir_ = sec_;
}
double distance(int i_, int j_) {
  double xi = node[i_].x, yi = node[i_].y;
  double xj = node[j_].x, yj = node[j_].y;
  return sqrt((xi - xj) * (xi - xj) + (yi - yj) * (yi - yj));
}
int Query(int l_, int mid_node, int r_) {
  llast = mid_node + 1;
  rlast = mid_node;
  double mid_nodex = node[mid_node].x;
  for (int l = l_, r = mid_node; l <= r; ) {
    int mid = (l + r) / 2;
    if (node[mid].x + ans > mid_nodex) {
      llast = mid;
      r = mid - 1;
    } else {
      l = mid + 1;
    }
  }
  for (int l = mid_node + 1, r = r_; l <= r; ) {
    int mid = (l + r) / 2;
    if (node[mid].x - ans < mid_nodex) {
      rlast = mid;
      l = mid + 1;
    } else {
      r = mid - 1;
    }
  }
}
void Solve(int l_, int r_) {
  if (l_ == r_) return ;
  int mid_node = (l_ + r_) / 2;
  Solve(l_, mid_node); Solve(mid_node + 1, r_);
  Query(l_, mid_node, r_);
  for (int l = llast; l <= mid_node; ++ l) {
    for (int r = rlast; r > mid_node; -- r) {
      Getmin(ans, distance(l, r));
    }
  }
}
//=============================================================
int main() {
  n = read();
  for (int i = 1; i <= n; ++ i) {
    scanf("%lf %lf", &node[i].x, &node[i].y);
  }
  std :: sort(node + 1, node + n + 1);
  Solve(1, n);
  printf("%.4lf", ans);
  return 0;
}
posted @ 2020-09-07 21:37  Luckyblock  阅读(215)  评论(0编辑  收藏  举报