cplib-cpp

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub hitonanode/cplib-cpp

:warning: Dyadic rational, surreal number (二進分数・固定小数点数,Conway の構成)
(number/dyadic_rational.hpp)

分母が二冪の有理数 DyadicRational<Int, Uint = unsigned long long> の実装,また超現実数の構成法に基づく $\{ l \mid r \}$ の計算.組合せゲーム理論などへの応用がある.

Int が整数部分を表すための符号付整数型で intlong long の利用を想定,Uint が小数部分を表すための符号なし整数型で unsignedunsigned long long の利用を想定(__uint128_t も動くかもしれない).メンバ変数 integ, frac がそれぞれ整数部分と小数部分に対応し,

$\displaystyle x = \mathrm{integ} + \frac{\mathrm{frac}}{2^\mathrm{FracLen}} $

がこのクラスのインスタンスが表す有理数 $x$ である. 実装の制約上分母のオーダーは(Uint = unsigned long long の場合)$2^{63}$ が上限となる.

使用方法

DyadicRational<Int>(Int x)

コンストラクタ.整数 x に対応する元を生成する.

double to_double()

インスタンスが表す有理数 $x$ を double 型に変換.

DyadicRational<Int> right()

Surreal number (超現実数)の構成過程を表す木 において,現在の値 $x$ の右側の子の値を返す.

DyadicRational<Int> left()

right() とは逆に,現在の値 $x$ の左側の子の値を返す.

DyadicRational<Int> DyadicRational<Int>::surreal(DyadicRational<Int> l, DyadicRational<Int> r)

$l < r$ を満たす二進分数 $l$, $r$ について,$l < x < r$ を満たし surreal number の構成過程を表す木で根($0$)に最も近い要素(Conway の記法に基づくと $\{ l \mid r \}$)の値を返す.$l < r$ を満たさない場合 runtime error となる.

現在は根から一歩ずつ right() または left() で探索していく実装になっているが,場合分けと bit 演算を頑張れば $O(1)$ になると思われる.

問題例

リンク

Code

#include <cassert>
#include <limits>
#include <type_traits>

// Dyadic rational, surreal numbers (超現実数)
// https://atcoder.jp/contests/abc229/submissions/28887690
template <class Int, class Uint = unsigned long long> struct DyadicRational {
    Int integ; // 整数部分
    Uint frac; // 小数部分の分子

    static constexpr int FracLen = std::numeric_limits<Uint>::digits - 1; // 2^63
    static constexpr Uint denom = Uint(1) << FracLen;                     // 小数部分の分母

    DyadicRational(Int x = 0) : integ(x), frac(0) {
        static_assert(
            0 < FracLen and FracLen < std::numeric_limits<Uint>::digits, "FracLen value error");
        static_assert(std::is_signed<Int>::value == true, "Int must be signed");
    }
    static DyadicRational _construct(Int x, Uint frac_) {
        DyadicRational ret(x);
        ret.frac = frac_;
        return ret;
    }
    double to_double() const { return integ + double(frac) / denom; }

    // static DyadicRational from_rational(Int num, int lg_den);
    bool operator==(const DyadicRational &r) const { return integ == r.integ and frac == r.frac; }
    bool operator!=(const DyadicRational &r) const { return integ != r.integ or frac != r.frac; }
    bool operator<(const DyadicRational &r) const {
        return integ != r.integ ? integ < r.integ : frac < r.frac;
    }
    bool operator<=(const DyadicRational &r) const { return (*this == r) or (*this < r); }
    bool operator>(const DyadicRational &r) const { return r < *this; }
    bool operator>=(const DyadicRational &r) const { return r <= *this; }

    DyadicRational operator+(const DyadicRational &r) const {
        auto i = integ + r.integ;
        auto f = frac + r.frac;
        if (f >= denom) ++i, f -= denom; // overflow
        return DyadicRational::_construct(i, f);
    }
    DyadicRational operator-(const DyadicRational &r) const {
        auto i = integ - r.integ;
        auto f = frac - r.frac;
        if (f > denom) --i, f += denom; // overflow
        return DyadicRational::_construct(i, f);
    }
    DyadicRational operator-() const { return DyadicRational() - *this; }
    DyadicRational &operator+=(const DyadicRational &r) { return *this = *this + r; }

    DyadicRational right() const {
        if (frac == 0) {
            if (integ >= 0) {
                return DyadicRational(integ + 1);
            } else {
                return DyadicRational::_construct(integ, Uint(1) << (FracLen - 1));
            }
        }
        int d = __builtin_ctzll(frac);
        return DyadicRational::_construct(integ, frac ^ (Uint(1) << (d - 1)));
    }
    DyadicRational left() const {
        if (frac == 0) {
            if (integ <= 0) {
                return DyadicRational(integ - 1);
            } else {
                return DyadicRational::_construct(integ - 1, Uint(1) << (FracLen - 1));
            }
        }
        int d = __builtin_ctzll(frac);
        return DyadicRational::_construct(integ, frac ^ (Uint(3) << (d - 1)));
    }

    // Surreal number { l | r }
    static DyadicRational surreal(const DyadicRational &l, const DyadicRational &r) {
        assert(l < r);
        DyadicRational x(0);
        if (l.integ > 0) x = DyadicRational(l.integ);
        if (r.integ < 0) x = DyadicRational(r.integ);
        while (true) {
            if (x <= l) {
                x = x.right();
            } else if (x >= r) {
                x = x.left();
            } else {
                break;
            }
        }
        return x;
    }
    template <class OStream> friend OStream &operator<<(OStream &os, const DyadicRational &x) {
        return os << x.to_double();
    }
};
// using dyrational = DyadicRational<long long>;
#line 1 "number/dyadic_rational.hpp"
#include <cassert>
#include <limits>
#include <type_traits>

// Dyadic rational, surreal numbers (超現実数)
// https://atcoder.jp/contests/abc229/submissions/28887690
template <class Int, class Uint = unsigned long long> struct DyadicRational {
    Int integ; // 整数部分
    Uint frac; // 小数部分の分子

    static constexpr int FracLen = std::numeric_limits<Uint>::digits - 1; // 2^63
    static constexpr Uint denom = Uint(1) << FracLen;                     // 小数部分の分母

    DyadicRational(Int x = 0) : integ(x), frac(0) {
        static_assert(
            0 < FracLen and FracLen < std::numeric_limits<Uint>::digits, "FracLen value error");
        static_assert(std::is_signed<Int>::value == true, "Int must be signed");
    }
    static DyadicRational _construct(Int x, Uint frac_) {
        DyadicRational ret(x);
        ret.frac = frac_;
        return ret;
    }
    double to_double() const { return integ + double(frac) / denom; }

    // static DyadicRational from_rational(Int num, int lg_den);
    bool operator==(const DyadicRational &r) const { return integ == r.integ and frac == r.frac; }
    bool operator!=(const DyadicRational &r) const { return integ != r.integ or frac != r.frac; }
    bool operator<(const DyadicRational &r) const {
        return integ != r.integ ? integ < r.integ : frac < r.frac;
    }
    bool operator<=(const DyadicRational &r) const { return (*this == r) or (*this < r); }
    bool operator>(const DyadicRational &r) const { return r < *this; }
    bool operator>=(const DyadicRational &r) const { return r <= *this; }

    DyadicRational operator+(const DyadicRational &r) const {
        auto i = integ + r.integ;
        auto f = frac + r.frac;
        if (f >= denom) ++i, f -= denom; // overflow
        return DyadicRational::_construct(i, f);
    }
    DyadicRational operator-(const DyadicRational &r) const {
        auto i = integ - r.integ;
        auto f = frac - r.frac;
        if (f > denom) --i, f += denom; // overflow
        return DyadicRational::_construct(i, f);
    }
    DyadicRational operator-() const { return DyadicRational() - *this; }
    DyadicRational &operator+=(const DyadicRational &r) { return *this = *this + r; }

    DyadicRational right() const {
        if (frac == 0) {
            if (integ >= 0) {
                return DyadicRational(integ + 1);
            } else {
                return DyadicRational::_construct(integ, Uint(1) << (FracLen - 1));
            }
        }
        int d = __builtin_ctzll(frac);
        return DyadicRational::_construct(integ, frac ^ (Uint(1) << (d - 1)));
    }
    DyadicRational left() const {
        if (frac == 0) {
            if (integ <= 0) {
                return DyadicRational(integ - 1);
            } else {
                return DyadicRational::_construct(integ - 1, Uint(1) << (FracLen - 1));
            }
        }
        int d = __builtin_ctzll(frac);
        return DyadicRational::_construct(integ, frac ^ (Uint(3) << (d - 1)));
    }

    // Surreal number { l | r }
    static DyadicRational surreal(const DyadicRational &l, const DyadicRational &r) {
        assert(l < r);
        DyadicRational x(0);
        if (l.integ > 0) x = DyadicRational(l.integ);
        if (r.integ < 0) x = DyadicRational(r.integ);
        while (true) {
            if (x <= l) {
                x = x.right();
            } else if (x >= r) {
                x = x.left();
            } else {
                break;
            }
        }
        return x;
    }
    template <class OStream> friend OStream &operator<<(OStream &os, const DyadicRational &x) {
        return os << x.to_double();
    }
};
// using dyrational = DyadicRational<long long>;
Back to top page