洛谷 P7561 道路建设 做题笔记

发布于

题意

给定 $n$ 个点 $\left(x_i, y_i\right)$,求曼哈顿距离下前 $k$ 小的点对。输出他们的距离。

$2 \leq n \leq 2.5 \times 10^5$,时限 $9$ 秒。

题解

做法 1

暴力的做法是维护一个堆,枚举点 $u$,再枚举 $v$,然后计算 $u$ 到 $v$ 的距离,插入到堆中,并且始终保持堆的大小在 $k$ 以内。

考虑用 K-D Tree 优化这个过程。如果堆的大小已经达到 $k$,并且 $u$ 到 $v$ 的距离大于堆中的最大元素,那么 $\left(u,v\right)$ 就必然不会是答案。

2-D Tree 上一个结点代表某个矩形以及这个矩形中按某一维排序的中位点。考虑将枚举 $v$ 的过程改成在 2-D Tree 上 DFS。如果我们发现当前 DFS 到的矩形的四个角与 $u$ 的距离的最小值大于堆中的最大元素(且堆的大小为 $k$)那么我们就可以直接回溯了。

算法复杂度未知。

能过,而且跑得飞快,甚至拿到了次优解。(2022/07/23)

代码

// Please submit with C++14!
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
#define ra _Read()

using tp = long long;
tp _Read() {
  bool __neg = 0;
  char __c = getchar();
  tp __val = 0;
  for (; __c < 48 || __c > 57; __c = getchar()) {
    __neg = __c == 45;
  }
  for (; __c > 47 && __c < 58; __c = getchar()) {
    __val = __val * 10 + (__c & 15);
  }
  return __neg ? ~__val + 1 : __val;
}
using namespace std;
constexpr tp Hat_N = 2.5e5 + 3;

struct Loc {
  tp x, y;
};

tp k, root;
array<Loc, Hat_N> p;
array<tp, Hat_N> ls, rs, lx, rx, ly, ry;

bool Lcomp_x(const Loc& x, const Loc& y) {
  if (x.x != y.x) {
    return x.x < y.x;
  }
  return x.y < y.y;
}

bool Lcomp_y(const Loc& x, const Loc& y) {
  if (x.y != y.y) {
    return x.y < y.y;
  }
  return x.x < y.x;
}

void maintain(tp x, tp y) {
  lx[x] = min(lx[x], lx[y]);
  rx[x] = max(rx[x], rx[y]);
  ly[x] = min(ly[x], ly[y]);
  ry[x] = max(ry[x], ry[y]);
}

tp build(tp l, tp r, bool xy = 1) {
  if (l > r) {
    return 0;
  } else {
    tp c = l + r >> 1;
    nth_element(&p[l], &p[c], &p[r + 1], xy ? Lcomp_x : Lcomp_y);
    lx[c] = rx[c] = p[c].x;
    ly[c] = ry[c] = p[c].y;
    if (ls[c] = build(l, c - 1, !xy)) {
      maintain(c, ls[c]);
    }
    if (rs[c] = build(c + 1, r, !xy)) {
      maintain(c, rs[c]);
    }
    return c;
  }
}

tp dist(tp x, tp y) {
  tp dx, dy;
  dx = p[x].x < lx[y] ? lx[y] - p[x].x : p[x].x > rx[y] ? p[x].x - rx[y] : 0;
  dy = p[x].y < ly[y] ? ly[y] - p[x].y : p[x].y > ry[y] ? p[x].y - ry[y] : 0;
  return dx + dy;
}

struct K_heap {
  priority_queue<tp> mink;

  void push(tp x, tp y) {
    tp dist = abs(p[x].x - p[y].x) + abs(p[x].y - p[y].y);
    if (mink.size() == k) {
      if (mink.top() > dist) {
        mink.pop();
        mink.push(dist);
      }
    } else {
      mink.push(dist);
    }
  }

  bool abandon(tp d) { return mink.size() == k && mink.top() <= d; }

  void print() {
    list<tp> r;
    while (mink.size()) {
      r.push_front(mink.top());
      mink.pop();
    }
    for (auto&& i : r) {
      printf("%lld\n", i);
    }
  }
} khp;

void calc(tp x, tp y) {
  if (y && !khp.abandon(dist(x, y))) {
    if (x < y) {
      khp.push(x, y);
    }
    calc(x, ls[y]);
    calc(x, rs[y]);
  }
}

void dfs(tp x) {
  if (x) {
    calc(x, root);
    dfs(ls[x]);
    dfs(rs[x]);
  }
}

signed main() {
  tp n = ra;
  k = ra;
  for (tp i = 1; i <= n; ++i) {
    p[i].x = ra;
    p[i].y = ra;
  }
  dfs(root = build(1, n));
  khp.print();
  return EXIT_SUCCESS;
}

/*#################################################################
#.................................................................#
#............................This.Code.Was.Created.By.RBTree......#
#.............#......#...............Limiting-Factor..............#
#............#.#....#.#.................Soul-Code.................#
#.............########............................................#
#............#........#..##############################...........#
#...........#..V....V......#..#........................#..#...#...#
#............#........#....#..........###..###..........#..#.#.#..#
#............#..X##X..#..#............#....#.#...........#..#...#.#
#...........#...N##N...#..#...........###..###..........#.........#
#.......MOE..#..@.....#....#.#.#.#...................#.#..........#
#.............########.....#.#.#.##############.#.#..#.#..........#
#..........................#.#.#.#.............#.#.#.#.#..........#
#......#########...........#.#.#.#.................#.#.#..........#
#.....#.........#..........#.#.#.#.................#.#.#..........#
#.#.#.#G#R#A#S#S#.#.#......#.#.#.#.................#.#.#..........#
#.###################......#.#.#.#.................#.#.#..........#
#...........................#.#.#...................#.#...........#
#.................................................................#
#################################################################*/

做法 2

把曼哈顿距离转成切比雪夫距离,然后二分。问题可以转化为求方形内的点数,可以二维数点。

复杂度 $\mathcal O\left(n \log^2 n\right)$

代码

口糊的,没有代码。


暂无评论

发表评论