algo — C++23 日本語リファレンス GitHub

検証済み · 最終更新 2026-09-07 · GitHub

座標圧縮

lib/algorithm/coordinate_compression.hpp

インクルード

#include "algorithm/coordinate_compression.hpp"

クラス

名前 説明
CoordinateCompression 座標圧縮

関数

名前 説明
compress 座標圧縮

検証

このヘッダを使うライブラリ(3)
検証プログラム(10)
実装
#pragma once
#include <algorithm>
#include <cassert>
#include <iterator>
#include <vector>

/// @brief 座標圧縮
/// @tparam T 座標の型
/// @complexity 型宣言に実行時計算量はない
template <class T>
struct CoordinateCompression {
    /// @brief 空の座標圧縮器を構築する
    /// @complexity $O(1)$
    CoordinateCompression() = default;

    /// @brief 与えた列から座標圧縮器を構築する
    /// @param _data 圧縮対象の列
    /// @complexity 要素数を $n$ として $O(n\log n)$
    CoordinateCompression(const std::vector<T> &_data) : data(_data) { build(); }

    /// @brief 圧縮後の番号から元の値を返す
    /// @complexity $O(1)$
    const T &operator[](int i) const { return data[i]; }

    /// @brief 最小の登録値を返す
    /// @complexity $O(1)$
    T front() const { return data.front(); }

    /// @brief 最大の登録値を返す
    /// @complexity $O(1)$
    T back() const { return data.back(); }

    /// @brief build前の列へ値を追加する
    /// @complexity 償却 $O(1)$
    void add(T x) { data.emplace_back(x); }

    /// @brief 登録値をソートして重複を除く
    /// @complexity 登録数を $n$ として $O(n\log n)$
    void build() {
        std::sort(data.begin(), data.end());
        data.erase(std::unique(data.begin(), data.end()), data.end());
    }

    /// @brief 値が登録されているか返す
    /// @complexity $O(\log n)$
    bool contains(T x) const {
        auto it = std::lower_bound(data.begin(), data.end(), x);
        return it != data.end() && *it == x;
    }

    /// @brief 登録された値の圧縮番号を返す
    /// @details `x` は登録済みでなければならない。未登録の値を含む境界を扱うなら
    ///          `lower_bound` / `upper_bound` を使う。
    /// @complexity $O(\log n)$
    int get(T x) const {
        assert(contains(x));
        return lower_bound(x);
    }

    /// @brief 値以上となる最初の圧縮番号を返す
    /// @details `x` は登録済みでなくてよい。半開区間の左端に使う。
    /// @complexity $O(\log n)$
    int lower_bound(T x) const { return std::distance(data.begin(), std::lower_bound(data.begin(), data.end(), x)); }

    /// @brief 値より大きくなる最初の圧縮番号を返す
    /// @details `x` は登録済みでなくてよい。閉区間の右端に使う。
    /// @complexity $O(\log n)$
    int upper_bound(T x) const { return std::distance(data.begin(), std::upper_bound(data.begin(), data.end(), x)); }

    /// @brief 列の各値を圧縮番号へ変換する
    /// @details 各要素は登録済みでなければならない。
    /// @complexity 入力長を $k$ として $O(k\log n)$
    std::vector<int> compress(const std::vector<T> &v) const {
        int n = v.size();
        std::vector<int> res(n);
        for (int i = 0; i < n; ++i) res[i] = get(v[i]);
        return res;
    }

    /// @brief 異なる登録値の個数を返す
    /// @complexity $O(1)$
    int size() const { return data.size(); }

  private:
    std::vector<T> data;
};

/// @brief 座標圧縮
/// @param v 圧縮対象の列
/// @return 各要素を昇順の圧縮番号へ変換した列
/// @complexity 要素数を $n$ として $O(n\log n)$
template <class T>
std::vector<int> compress(const std::vector<T> &v) {
    return CoordinateCompression(v).compress(v);
}

bundled

#line 2 "lib/algorithm/coordinate_compression.hpp"
#include <algorithm>
#include <cassert>
#include <iterator>
#include <vector>

/// @brief 座標圧縮
/// @tparam T 座標の型
/// @complexity 型宣言に実行時計算量はない
template <class T>
struct CoordinateCompression {
    /// @brief 空の座標圧縮器を構築する
    /// @complexity $O(1)$
    CoordinateCompression() = default;

    /// @brief 与えた列から座標圧縮器を構築する
    /// @param _data 圧縮対象の列
    /// @complexity 要素数を $n$ として $O(n\log n)$
    CoordinateCompression(const std::vector<T> &_data) : data(_data) { build(); }

    /// @brief 圧縮後の番号から元の値を返す
    /// @complexity $O(1)$
    const T &operator[](int i) const { return data[i]; }

    /// @brief 最小の登録値を返す
    /// @complexity $O(1)$
    T front() const { return data.front(); }

    /// @brief 最大の登録値を返す
    /// @complexity $O(1)$
    T back() const { return data.back(); }

    /// @brief build前の列へ値を追加する
    /// @complexity 償却 $O(1)$
    void add(T x) { data.emplace_back(x); }

    /// @brief 登録値をソートして重複を除く
    /// @complexity 登録数を $n$ として $O(n\log n)$
    void build() {
        std::sort(data.begin(), data.end());
        data.erase(std::unique(data.begin(), data.end()), data.end());
    }

    /// @brief 値が登録されているか返す
    /// @complexity $O(\log n)$
    bool contains(T x) const {
        auto it = std::lower_bound(data.begin(), data.end(), x);
        return it != data.end() && *it == x;
    }

    /// @brief 登録された値の圧縮番号を返す
    /// @details `x` は登録済みでなければならない。未登録の値を含む境界を扱うなら
    ///          `lower_bound` / `upper_bound` を使う。
    /// @complexity $O(\log n)$
    int get(T x) const {
        assert(contains(x));
        return lower_bound(x);
    }

    /// @brief 値以上となる最初の圧縮番号を返す
    /// @details `x` は登録済みでなくてよい。半開区間の左端に使う。
    /// @complexity $O(\log n)$
    int lower_bound(T x) const { return std::distance(data.begin(), std::lower_bound(data.begin(), data.end(), x)); }

    /// @brief 値より大きくなる最初の圧縮番号を返す
    /// @details `x` は登録済みでなくてよい。閉区間の右端に使う。
    /// @complexity $O(\log n)$
    int upper_bound(T x) const { return std::distance(data.begin(), std::upper_bound(data.begin(), data.end(), x)); }

    /// @brief 列の各値を圧縮番号へ変換する
    /// @details 各要素は登録済みでなければならない。
    /// @complexity 入力長を $k$ として $O(k\log n)$
    std::vector<int> compress(const std::vector<T> &v) const {
        int n = v.size();
        std::vector<int> res(n);
        for (int i = 0; i < n; ++i) res[i] = get(v[i]);
        return res;
    }

    /// @brief 異なる登録値の個数を返す
    /// @complexity $O(1)$
    int size() const { return data.size(); }

  private:
    std::vector<T> data;
};

/// @brief 座標圧縮
/// @param v 圧縮対象の列
/// @return 各要素を昇順の圧縮番号へ変換した列
/// @complexity 要素数を $n$ として $O(n\log n)$
template <class T>
std::vector<int> compress(const std::vector<T> &v) {
    return CoordinateCompression(v).compress(v);
}