Skip to the content.

:heavy_check_mark: data_structure/kth_element.hpp

Depends on

Required by

Verified with

Code

#pragma once


#include <cassert>
#include <vector>
#include <functional>
#include <queue>
#include <optional>
#include <concepts>
#include <ranges>


#include "internal/dev_env.hpp"
#include "internal/types.hpp"


namespace uni {


// Thanks to: https://qiita.com/drken/items/1b7e6e459c24a83bb7fd
template<
    class T,
    std::ranges::output_range<T> Container = std::vector<T>,
    std::strict_weak_order<T, T> Comparer = std::less<T>,
    std::strict_weak_order<T, T> RevComparer = std::greater<T>,
    std::integral Size = internal::size_t
>
struct kth_element {
    using value_type = T;
    using size_type = Size;

  protected:
    size_type _k;
    std::priority_queue<T, Container, Comparer> _small;
    std::priority_queue<T, Container, RevComparer> _large;

  public:
    kth_element(const size_type k = 0) noexcept(NO_EXCEPT) : _k(k + 1) { assert(k >= 0); }

    inline bool has() const noexcept(NO_EXCEPT) { return std::ssize(this->_small) == this->_k; }

    inline value_type value() const noexcept(NO_EXCEPT) { return this->_small.top(); }

    inline std::optional<value_type> get() const noexcept(NO_EXCEPT) {
        if(this->has()) return this->value();
        return {};
    }

    template<std::convertible_to<T> U = T>
    inline auto value_or(U&& v) const noexcept(NO_EXCEPT) {
        return this->get().value_or(std::forward<U>(v));
    }

    inline void push(const value_type& v) noexcept(NO_EXCEPT) {
        if(std::ssize(this->_small) < this->_k) {
            this->_small.push(v);
            return;
        }

        const auto kth = this->_small.top();

        if(Comparer{}(v, kth)) {
            this->_small.pop();
            this->_small.push(v);
            this->_large.push(kth);
        }
        else {
            this->_large.push(v);
        }
    }

    inline void pop() noexcept(NO_EXCEPT) {
        assert(this->has());

        this->_small.pop();

        if(this->_large.empty()) return;

        const auto v = this->_large.top();

        this->_large.pop();
        this->_small.push(v);
    }
};


} // namespace uni
#line 2 "data_structure/kth_element.hpp"


#include <cassert>
#include <vector>
#include <functional>
#include <queue>
#include <optional>
#include <concepts>
#include <ranges>


#line 2 "internal/dev_env.hpp"


#ifdef LOCAL_JUDGE
    inline constexpr bool DEV_ENV = true;
    inline constexpr bool NO_EXCEPT = false;
#else
    inline constexpr bool DEV_ENV = false;
    inline constexpr bool NO_EXCEPT = true;
#endif // LOCAL_JUDGE


#if __cplusplus >= 202100L
    #define CPP20 true
    #define CPP23 true
#elif __cplusplus >= 202002L
    #define CPP20 true
    #define CPP23 false
#else
    #define CPP20 false
    #define CPP23 false
#endif
#line 2 "internal/types.hpp"

#include <cstdint>

namespace uni {

namespace internal {


using size_t = std::int64_t;

using int128_t = __int128_t;
using uint128_t = __uint128_t;


} // namesapce internal

} // namespace uni
#line 15 "data_structure/kth_element.hpp"


namespace uni {


// Thanks to: https://qiita.com/drken/items/1b7e6e459c24a83bb7fd
template<
    class T,
    std::ranges::output_range<T> Container = std::vector<T>,
    std::strict_weak_order<T, T> Comparer = std::less<T>,
    std::strict_weak_order<T, T> RevComparer = std::greater<T>,
    std::integral Size = internal::size_t
>
struct kth_element {
    using value_type = T;
    using size_type = Size;

  protected:
    size_type _k;
    std::priority_queue<T, Container, Comparer> _small;
    std::priority_queue<T, Container, RevComparer> _large;

  public:
    kth_element(const size_type k = 0) noexcept(NO_EXCEPT) : _k(k + 1) { assert(k >= 0); }

    inline bool has() const noexcept(NO_EXCEPT) { return std::ssize(this->_small) == this->_k; }

    inline value_type value() const noexcept(NO_EXCEPT) { return this->_small.top(); }

    inline std::optional<value_type> get() const noexcept(NO_EXCEPT) {
        if(this->has()) return this->value();
        return {};
    }

    template<std::convertible_to<T> U = T>
    inline auto value_or(U&& v) const noexcept(NO_EXCEPT) {
        return this->get().value_or(std::forward<U>(v));
    }

    inline void push(const value_type& v) noexcept(NO_EXCEPT) {
        if(std::ssize(this->_small) < this->_k) {
            this->_small.push(v);
            return;
        }

        const auto kth = this->_small.top();

        if(Comparer{}(v, kth)) {
            this->_small.pop();
            this->_small.push(v);
            this->_large.push(kth);
        }
        else {
            this->_large.push(v);
        }
    }

    inline void pop() noexcept(NO_EXCEPT) {
        assert(this->has());

        this->_small.pop();

        if(this->_large.empty()) return;

        const auto v = this->_large.top();

        this->_large.pop();
        this->_small.push(v);
    }
};


} // namespace uni
Back to top page