Skip to content

cdq_dominance.hpp

SECTIONData Structure INCLUDEnoya/cdq_dominance.hpp

For each 3D point, count input points coordinate-wise no greater than it, including itself and duplicate copies, in O(n log^2 n).

用 CDQ 分治统计多维偏序贡献;适合二维/三维点对计数、前缀转移和带时间顺序的离线问题。

Implementation

View on GitHub

#ifndef NOYA_CDQ_DOMINANCE_HPP
#define NOYA_CDQ_DOMINANCE_HPP 1

/// @complexity Time: O(n log^2 n).
/// Space: O(n).

#include <algorithm>
#include <array>
#include <cstdint>
#include <numeric>
#include <tuple>
#include <utility>
#include <vector>

namespace noya {

/// @brief For each 3D point, count input points coordinate-wise no greater than
/// it, including itself and duplicate copies, in O(n log^2 n).
template <class T>
std::vector<std::int64_t>
dominance_count_3d(const std::vector<std::array<T, 3>> &points) {
  struct item {
    T x;
    T y;
    T z;
    int z_rank = 0;
    std::int64_t multiplicity = 0;
    std::int64_t answer = 0;
    std::vector<int> ids;
  };

  std::vector<int> order(points.size());
  for (int index = 0; index < int(points.size()); index++) {
    order[index] = index;
  }
  std::sort(order.begin(), order.end(), [&](int first, int second) {
    return points[first] < points[second];
  });
  std::vector<item> items;
  for (int id : order) {
    auto [x, y, z] = points[id];
    if (items.empty() || std::tie(items.back().x, items.back().y,
                                  items.back().z) != std::tie(x, y, z)) {
      items.push_back({x, y, z});
    }
    items.back().multiplicity++;
    items.back().ids.push_back(id);
  }
  std::vector<T> z_values;
  for (const item &value : items) {
    z_values.push_back(value.z);
  }
  std::sort(z_values.begin(), z_values.end());
  z_values.erase(std::unique(z_values.begin(), z_values.end()), z_values.end());
  for (item &value : items) {
    value.z_rank =
        int(std::lower_bound(z_values.begin(), z_values.end(), value.z) -
            z_values.begin());
    value.answer = value.multiplicity;
  }

  std::vector<std::int64_t> fenwick(z_values.size() + 1);
  auto add = [&](int position, std::int64_t delta) {
    for (position++; position < int(fenwick.size());
         position += position & -position) {
      fenwick[position] += delta;
    }
  };
  auto prefix = [&](int right) {
    std::int64_t result = 0;
    for (; right > 0; right -= right & -right) {
      result += fenwick[right];
    }
    return result;
  };
  std::vector<item> buffer(items.size());
  auto solve = [&](auto &self, int left, int right) -> void {
    if (right - left <= 1) {
      return;
    }
    int middle = std::midpoint(left, right);
    self(self, left, middle);
    self(self, middle, right);
    int first = left;
    for (int second = middle; second < right; second++) {
      while (first < middle && !(items[second].y < items[first].y)) {
        add(items[first].z_rank, items[first].multiplicity);
        first++;
      }
      items[second].answer += prefix(items[second].z_rank + 1);
    }
    for (int index = left; index < first; index++) {
      add(items[index].z_rank, -items[index].multiplicity);
    }
    std::merge(items.begin() + left, items.begin() + middle,
               items.begin() + middle, items.begin() + right,
               buffer.begin() + left, [](const item &a, const item &b) {
                 return std::tie(a.y, a.z) < std::tie(b.y, b.z);
               });
    std::move(buffer.begin() + left, buffer.begin() + right,
              items.begin() + left);
  };
  solve(solve, 0, int(items.size()));

  std::vector<std::int64_t> result(points.size());
  for (const item &value : items) {
    for (int id : value.ids) {
      result[id] = value.answer;
    }
  }
  return result;
}

} // namespace noya

#endif // NOYA_CDQ_DOMINANCE_HPP
#include <algorithm>
#include <array>
#include <cstdint>
#include <numeric>
#include <tuple>
#include <utility>
#include <vector>

/// @complexity Time: O(n log^2 n).
/// Space: O(n).

namespace noya {

/// @brief For each 3D point, count input points coordinate-wise no greater than
/// it, including itself and duplicate copies, in O(n log^2 n).
template <class T>
std::vector<std::int64_t>
dominance_count_3d(const std::vector<std::array<T, 3>> &points) {
  struct item {
    T x;
    T y;
    T z;
    int z_rank = 0;
    std::int64_t multiplicity = 0;
    std::int64_t answer = 0;
    std::vector<int> ids;
  };

  std::vector<int> order(points.size());
  for (int index = 0; index < int(points.size()); index++) {
    order[index] = index;
  }
  std::sort(order.begin(), order.end(), [&](int first, int second) {
    return points[first] < points[second];
  });
  std::vector<item> items;
  for (int id : order) {
    auto [x, y, z] = points[id];
    if (items.empty() || std::tie(items.back().x, items.back().y,
                                  items.back().z) != std::tie(x, y, z)) {
      items.push_back({x, y, z});
    }
    items.back().multiplicity++;
    items.back().ids.push_back(id);
  }
  std::vector<T> z_values;
  for (const item &value : items) {
    z_values.push_back(value.z);
  }
  std::sort(z_values.begin(), z_values.end());
  z_values.erase(std::unique(z_values.begin(), z_values.end()), z_values.end());
  for (item &value : items) {
    value.z_rank =
        int(std::lower_bound(z_values.begin(), z_values.end(), value.z) -
            z_values.begin());
    value.answer = value.multiplicity;
  }

  std::vector<std::int64_t> fenwick(z_values.size() + 1);
  auto add = [&](int position, std::int64_t delta) {
    for (position++; position < int(fenwick.size());
         position += position & -position) {
      fenwick[position] += delta;
    }
  };
  auto prefix = [&](int right) {
    std::int64_t result = 0;
    for (; right > 0; right -= right & -right) {
      result += fenwick[right];
    }
    return result;
  };
  std::vector<item> buffer(items.size());
  auto solve = [&](auto &self, int left, int right) -> void {
    if (right - left <= 1) {
      return;
    }
    int middle = std::midpoint(left, right);
    self(self, left, middle);
    self(self, middle, right);
    int first = left;
    for (int second = middle; second < right; second++) {
      while (first < middle && !(items[second].y < items[first].y)) {
        add(items[first].z_rank, items[first].multiplicity);
        first++;
      }
      items[second].answer += prefix(items[second].z_rank + 1);
    }
    for (int index = left; index < first; index++) {
      add(items[index].z_rank, -items[index].multiplicity);
    }
    std::merge(items.begin() + left, items.begin() + middle,
               items.begin() + middle, items.begin() + right,
               buffer.begin() + left, [](const item &a, const item &b) {
                 return std::tie(a.y, a.z) < std::tie(b.y, b.z);
               });
    std::move(buffer.begin() + left, buffer.begin() + right,
              items.begin() + left);
  };
  solve(solve, 0, int(items.size()));

  std::vector<std::int64_t> result(points.size());
  for (const item &value : items) {
    for (int id : value.ids) {
      result[id] = value.answer;
    }
  }
  return result;
}

} // namespace noya