CP-Algorithms Library

This documentation is automatically generated by competitive-verifier/competitive-verifier

View the Project on GitHub cp-algorithms/cp-algorithms-aux

:heavy_check_mark: cp-algo/math/combinatorics.hpp

Depends on

Required by

Verified with

Code

#ifndef CP_ALGO_MATH_COMBINATORICS_HPP
#define CP_ALGO_MATH_COMBINATORICS_HPP
#include "../math/common.hpp"
#include "../util/big_alloc.hpp"
#include <cassert>
#include <ranges>
namespace cp_algo::math {
    // fact/rfact/small_inv are caching
    // Beware of usage with dynamic mod
    template<typename T>
    T fact(auto n) {
        static const auto F = [] {
            big_vector<T> F(maxn);
            F[0] = T(1);
            for(int i = 1; i < maxn; i++) {
                F[i] = F[i - 1] * T(i);
            }
            return F;
        }();
        return F[n];
    }
    // Only works for modint types
    template<typename T>
    T rfact(auto n) {
        static const auto F = [] {
            big_vector<T> F(maxn);
            int t = (int)std::min<int64_t>(T::mod(), maxn) - 1;
            F[t] = T(1) / fact<T>(t);
            for(int i = t - 1; i >= 0; i--) {
                F[i] = F[i + 1] * T(i + 1);
            }
            return F;
        }();
        return F[n];
    }
    template<typename T, int base>
    T pow_fixed(int n) {
        static const auto prec = [] {
            big_vector<T> prec_low(1 << 16), prec_high(1 << 16);
            prec_low[0] = prec_high[0] = T(1);
            T step_low = T(base);
            T step_high = bpow(T(base), 1 << 16);
            for(int i = 1; i < (1 << 16); i++) {
                prec_low[i] = prec_low[i - 1] * step_low;
                prec_high[i] = prec_high[i - 1] * step_high;
            }
            return std::pair{std::move(prec_low), std::move(prec_high)};
        }();
        return prec.first[n & 0xFFFF] * prec.second[n >> 16];
    }
    template<typename T>
    big_vector<T> bulk_invs(auto const& args) {
        big_vector<T> res(std::size(args), args[0]);
        for(size_t i = 1; i < std::size(args); i++) {
            res[i] = res[i - 1] * args[i];
        }
        auto all_invs = T(1) / res.back();
        for(size_t i = std::size(args) - 1; i > 0; i--) {
            res[i] = all_invs * res[i - 1];
            all_invs *= args[i];
        }
        res[0] = all_invs;
        return res;
    }
    template<typename T>
    T small_inv(auto n) {
        static auto F = bulk_invs<T>(std::views::iota(1, maxn));
        return F[n - 1];
    }
    template<typename T>
    T binom_large(T n, auto r) {
        assert(r < maxn);
        T ans = 1;
        for(decltype(r) i = 0; i < r; i++) {
            ans = ans * T(n - i) * small_inv<T>(i + 1);
        }
        return ans;
    }
    template<typename T>
    T binom(auto n, auto r) {
        if(r < 0 || r > n) {
            return T(0);
        } else if(n >= maxn) {
            return binom_large(T(n), r);
        } else {
            return fact<T>(n) * rfact<T>(r) * rfact<T>(n - r);
        }
    }
}
#endif // CP_ALGO_MATH_COMBINATORICS_HPP
#line 1 "cp-algo/math/combinatorics.hpp"


#line 1 "cp-algo/math/common.hpp"


#include <functional>
#include <cstdint>
#include <cassert>
#include <bit>
#include <vector>
#include <algorithm>
namespace cp_algo::math {
#ifdef CP_ALGO_MAXN
    const int maxn = CP_ALGO_MAXN;
#else
    const int maxn = 1 << 19;
#endif
    const int magic = 64; // threshold for sizes to run the naive algo

    // Nonnegative 64-bit exponents, with an associative operation and its identity.
    // Windows >1 precompute odd powers only when that saves operations.
    template<int window = 1>
    auto bpow(auto const& x, auto n, auto const& one, auto op) {
        static_assert(window >= 1 && window <= 6);
        if constexpr(window > 1) {
            if(n == 0) {return one;}
            int bits = std::bit_width(uint64_t(n));
            auto low_bit = [&](int high) {
                int low = std::max(0, high - window + 1);
                while(!((n >> low) & 1)) {low++;}
                return low;
            };
            int first = low_bit(bits - 1);
            int cost = (1 << (window - 1)) + first;
            for(int j = first - 1; j >= 0;) {
                if(!((n >> j) & 1)) {j--;}
                else {cost++; j = low_bit(j) - 1;}
            }
            // Do not pay for the table when binary powering uses fewer operations.
            if(cost >= bits + std::popcount(uint64_t(n)) - 2) {return bpow<1>(x, n, one, op);}
            using T = std::decay_t<decltype(x)>;
            std::vector<T> odd;
            odd.reserve(1 << (window - 1));
            odd.push_back(x);
            auto square = op(x, x);
            while(odd.size() < size_t(1 << (window - 1))) {odd.push_back(op(odd.back(), square));}
            auto ans = odd[(n >> first) / 2];
            for(int j = first - 1; j >= 0;) {
                if(!((n >> j) & 1)) {ans = op(ans, ans); j--;}
                else {
                    int low = low_bit(j), length = j - low + 1;
                    auto digit = (n >> low) & ((1u << length) - 1);
                    for(int i = 0; i < length; i++) {ans = op(ans, ans);}
                    ans = op(ans, odd[digit / 2]);
                    j = low - 1;
                }
            }
            return ans;
        } else {
            if (n == 0) {
                return one;
            }
            auto ans = x;
            for(int j = std::bit_width<uint64_t>(n) - 2; ~j; j--) {
                ans = op(ans, ans);
                if((n >> j) & 1) {
                    ans = op(ans, x);
                }
            }
            return ans;
        }
    }
    template<int window = 1>
    auto bpow(auto x, auto n, auto ans) {
        return bpow<window>(x, n, ans, std::multiplies{});
    }
    template<typename T>
    T bpow(T const& x, auto n) {
        return bpow(x, n, T(1));
    }
    inline constexpr auto inv2(auto x) {
        assert(x % 2);
        std::make_unsigned_t<decltype(x)> y = 1;
        while(y * x != 1) {
            y *= 2 - x * y;
        }
        return y;
    }
}

#line 1 "cp-algo/util/big_alloc.hpp"



#include <set>
#include <map>
#include <deque>
#include <stack>
#include <queue>
#line 10 "cp-algo/util/big_alloc.hpp"
#include <string>
#include <cstddef>
#line 13 "cp-algo/util/big_alloc.hpp"
#include <iostream>
#include <forward_list>

// Single macro to detect POSIX platforms (Linux, Unix, macOS)
#if defined(__linux__) || defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#define CP_ALGO_USE_MMAP 1
#include <sys/mman.h>
#else
#define CP_ALGO_USE_MMAP 0
#endif

namespace cp_algo {
    template<typename T, size_t Align = 32>
    class big_alloc {
        static_assert(Align >= alignof(void*), "Align must be at least pointer-size");
        static_assert(std::has_single_bit(Align), "Align must be a power of two");

    public:
        using value_type = T;
        template<class U>
        struct rebind {
            using other = big_alloc<U, Align>;
        };
        constexpr bool operator==(const big_alloc&) const = default;

        big_alloc() noexcept = default;
        template<typename U, std::size_t A>
        big_alloc(const big_alloc<U, A>&) noexcept {}

        [[nodiscard]] T* allocate(std::size_t n) {
            std::size_t padded = round_up(n * sizeof(T));
            std::size_t align = std::max<std::size_t>(alignof(T), Align);
#if CP_ALGO_USE_MMAP
            if(padded >= MEGABYTE) {
                void* raw = mmap(nullptr, padded, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
                madvise(raw, padded, MADV_HUGEPAGE);
                return static_cast<T*>(raw);
            }
#endif
            return static_cast<T*>(::operator new(padded, std::align_val_t(align)));
        }

        void deallocate(T* p, std::size_t n) noexcept {
            if(!p) {
                return;
            }
            std::size_t padded = round_up(n * sizeof(T));
            std::size_t align = std::max<std::size_t>(alignof(T), Align);
#if CP_ALGO_USE_MMAP
            if(padded >= MEGABYTE) {
                munmap(p, padded);
                return;
            }
#endif
            ::operator delete(p, padded, std::align_val_t(align));
        }

    private:
        static constexpr std::size_t MEGABYTE = 1 << 20;
        static constexpr std::size_t round_up(std::size_t x) noexcept {
            return (x + Align - 1) / Align * Align;
        }
    };

    template<typename T>
    using big_vector = std::vector<T, big_alloc<T>>;
    template<typename T>
    using big_basic_string = std::basic_string<T, std::char_traits<T>, big_alloc<T>>;
    template<typename T>
    using big_deque = std::deque<T, big_alloc<T>>;
    template<typename T>
    using big_stack = std::stack<T, big_deque<T>>;
    template<typename T>
    using big_queue = std::queue<T, big_deque<T>>;
    template<typename T>
    using big_priority_queue = std::priority_queue<T, big_vector<T>>;
    template<typename T>
    using big_forward_list = std::forward_list<T, big_alloc<T>>;
    using big_string = big_basic_string<char>;

    template<typename Key, typename Value, typename Compare = std::less<Key>>
    using big_map = std::map<Key, Value, Compare, big_alloc<std::pair<const Key, Value>>>;
    template<typename T, typename Compare = std::less<T>>
    using big_multiset = std::multiset<T, Compare, big_alloc<T>>;
    template<typename T, typename Compare = std::less<T>>
    using big_set = std::set<T, Compare, big_alloc<T>>;
}


#line 6 "cp-algo/math/combinatorics.hpp"
#include <ranges>
namespace cp_algo::math {
    // fact/rfact/small_inv are caching
    // Beware of usage with dynamic mod
    template<typename T>
    T fact(auto n) {
        static const auto F = [] {
            big_vector<T> F(maxn);
            F[0] = T(1);
            for(int i = 1; i < maxn; i++) {
                F[i] = F[i - 1] * T(i);
            }
            return F;
        }();
        return F[n];
    }
    // Only works for modint types
    template<typename T>
    T rfact(auto n) {
        static const auto F = [] {
            big_vector<T> F(maxn);
            int t = (int)std::min<int64_t>(T::mod(), maxn) - 1;
            F[t] = T(1) / fact<T>(t);
            for(int i = t - 1; i >= 0; i--) {
                F[i] = F[i + 1] * T(i + 1);
            }
            return F;
        }();
        return F[n];
    }
    template<typename T, int base>
    T pow_fixed(int n) {
        static const auto prec = [] {
            big_vector<T> prec_low(1 << 16), prec_high(1 << 16);
            prec_low[0] = prec_high[0] = T(1);
            T step_low = T(base);
            T step_high = bpow(T(base), 1 << 16);
            for(int i = 1; i < (1 << 16); i++) {
                prec_low[i] = prec_low[i - 1] * step_low;
                prec_high[i] = prec_high[i - 1] * step_high;
            }
            return std::pair{std::move(prec_low), std::move(prec_high)};
        }();
        return prec.first[n & 0xFFFF] * prec.second[n >> 16];
    }
    template<typename T>
    big_vector<T> bulk_invs(auto const& args) {
        big_vector<T> res(std::size(args), args[0]);
        for(size_t i = 1; i < std::size(args); i++) {
            res[i] = res[i - 1] * args[i];
        }
        auto all_invs = T(1) / res.back();
        for(size_t i = std::size(args) - 1; i > 0; i--) {
            res[i] = all_invs * res[i - 1];
            all_invs *= args[i];
        }
        res[0] = all_invs;
        return res;
    }
    template<typename T>
    T small_inv(auto n) {
        static auto F = bulk_invs<T>(std::views::iota(1, maxn));
        return F[n - 1];
    }
    template<typename T>
    T binom_large(T n, auto r) {
        assert(r < maxn);
        T ans = 1;
        for(decltype(r) i = 0; i < r; i++) {
            ans = ans * T(n - i) * small_inv<T>(i + 1);
        }
        return ans;
    }
    template<typename T>
    T binom(auto n, auto r) {
        if(r < 0 || r > n) {
            return T(0);
        } else if(n >= maxn) {
            return binom_large(T(n), r);
        } else {
            return fact<T>(n) * rfact<T>(r) * rfact<T>(n - r);
        }
    }
}

#ifndef CP_ALGO_MATH_COMBINATORICS_HPP
#define CP_ALGO_MATH_COMBINATORICS_HPP
#include "../math/common.hpp"
#include "../util/big_alloc.hpp"
#include <cassert>
#include <ranges>
namespace cp_algo::math{ template<typename T> T fact(auto n){ static const auto F= []{ big_vector<T>F(maxn); F[0] =T(1); for(int i=1;i<maxn;i++){ F[i] =F[i-1] *T(i); } return F; }(); return F[n]; } template<typename T> T rfact(auto n){ static const auto F= []{ big_vector<T>F(maxn); int t=(int)std::min<int64_t>(T::mod(),maxn)-1; F[t] =T(1)/fact<T>(t); for(int i=t-1;i>=0;i--){ F[i] =F[i+1] *T(i+1); } return F; }(); return F[n]; } template<typename T,int base> T pow_fixed(int n){ static const auto prec= []{ big_vector<T>prec_low(1<<16),prec_high(1<<16); prec_low[0] =prec_high[0] =T(1); T step_low=T(base); T step_high=bpow(T(base),1<<16); for(int i=1;i<(1<<16);i++){ prec_low[i] =prec_low[i-1] *step_low; prec_high[i] =prec_high[i-1] *step_high; } return std::pair{std::move(prec_low),std::move(prec_high)}; }(); return prec.first[n&0xFFFF] *prec.second[n>>16]; } template<typename T> big_vector<T>bulk_invs(auto const&args){ big_vector<T>res(std::size(args),args[0]); for(size_t i=1;i<std::size(args);i++){ res[i] =res[i-1] *args[i]; } auto all_invs=T(1)/res.back(); for(size_t i=std::size(args)-1;i>0;i--){ res[i] =all_invs*res[i-1]; all_invs*=args[i]; } res[0] =all_invs; return res; } template<typename T> T small_inv(auto n){ static auto F=bulk_invs<T>(std::views::iota(1,maxn)); return F[n-1]; } template<typename T> T binom_large(T n,auto r){ assert(r<maxn); T ans=1; for(decltype(r)i=0;i<r;i++){ ans=ans*T(n-i)*small_inv<T>(i+1); } return ans; } template<typename T> T binom(auto n,auto r){ if(r<0||r>n){ return T(0); }else if(n>=maxn){ return binom_large(T(n),r); }else{ return fact<T>(n)*rfact<T>(r)*rfact<T>(n-r); } } }
#endif
#line 1 "cp-algo/math/combinatorics.hpp"
#line 1 "cp-algo/math/common.hpp"
#include <functional>
#include <cstdint>
#include <cassert>
#include <bit>
#include <vector>
#include <algorithm>
namespace cp_algo::math{
#ifdef CP_ALGO_MAXN
const int maxn=CP_ALGO_MAXN;
#else
const int maxn=1<<19;
#endif
const int magic=64; template<int window=1> auto bpow(auto const&x,auto n,auto const&one,auto op){ static_assert(window>=1&&window<=6); if constexpr(window>1){ if(n==0){return one;} int bits=std::bit_width(uint64_t(n)); auto low_bit= [&](int high){ int low=std::max(0,high-window+1); while(!((n>>low)&1)){low++;} return low; }; int first=low_bit(bits-1); int cost=(1<<(window-1))+first; for(int j=first-1;j>=0;){ if(!((n>>j)&1)){j--;} else{cost++;j=low_bit(j)-1;} } if(cost>=bits+std::popcount(uint64_t(n))-2){return bpow<1>(x,n,one,op);} using T=std::decay_t<decltype(x)>; std::vector<T>odd; odd.reserve(1<<(window-1)); odd.push_back(x); auto square=op(x,x); while(odd.size()<size_t(1<<(window-1))){odd.push_back(op(odd.back(),square));} auto ans=odd[(n>>first)/2]; for(int j=first-1;j>=0;){ if(!((n>>j)&1)){ans=op(ans,ans);j--;} else{ int low=low_bit(j),length=j-low+1; auto digit=(n>>low)&((1u<<length)-1); for(int i=0;i<length;i++){ans=op(ans,ans);} ans=op(ans,odd[digit/2]); j=low-1; } } return ans; }else{ if(n==0){ return one; } auto ans=x; for(int j=std::bit_width<uint64_t>(n)-2;~j;j--){ ans=op(ans,ans); if((n>>j)&1){ ans=op(ans,x); } } return ans; } } template<int window=1> auto bpow(auto x,auto n,auto ans){ return bpow<window>(x,n,ans,std::multiplies{}); } template<typename T> T bpow(T const&x,auto n){ return bpow(x,n,T(1)); } inline constexpr auto inv2(auto x){ assert(x%2); std::make_unsigned_t<decltype(x)>y=1; while(y*x!=1){ y*=2-x*y; } return y; } }
#line 1 "cp-algo/util/big_alloc.hpp"
#include <set>
#include <map>
#include <deque>
#include <stack>
#include <queue>
#line 10 "cp-algo/util/big_alloc.hpp"
#include <string>
#include <cstddef>
#line 13 "cp-algo/util/big_alloc.hpp"
#include <iostream>
#include <forward_list>
#if defined(__linux__) || defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#define CP_ALGO_USE_MMAP 1
#include <sys/mman.h>
#else
#define CP_ALGO_USE_MMAP 0
#endif
namespace cp_algo{ template<typename T,size_t Align=32> class big_alloc{ static_assert(Align>=alignof(void*),"Align must be at least pointer-size"); static_assert(std::has_single_bit(Align),"Align must be a power of two"); public: using value_type=T; template<class U> struct rebind{ using other=big_alloc<U,Align>; }; constexpr bool operator==(const big_alloc&)const=default; big_alloc()noexcept=default; template<typename U,std::size_t A> big_alloc(const big_alloc<U,A>&)noexcept{} [[nodiscard]]T*allocate(std::size_t n){ std::size_t padded=round_up(n*sizeof(T)); std::size_t align=std::max<std::size_t>(alignof(T),Align);
#if CP_ALGO_USE_MMAP
if(padded>=MEGABYTE){ void*raw=mmap(nullptr,padded,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0); madvise(raw,padded,MADV_HUGEPAGE); return static_cast<T*>(raw); }
#endif
return static_cast<T*>(::operator new(padded,std::align_val_t(align))); } void deallocate(T*p,std::size_t n)noexcept{ if(!p){ return; } std::size_t padded=round_up(n*sizeof(T)); std::size_t align=std::max<std::size_t>(alignof(T),Align);
#if CP_ALGO_USE_MMAP
if(padded>=MEGABYTE){ munmap(p,padded); return; }
#endif
::operator delete(p,padded,std::align_val_t(align)); } private: static constexpr std::size_t MEGABYTE=1<<20; static constexpr std::size_t round_up(std::size_t x)noexcept{ return(x+Align-1)/Align*Align; } }; template<typename T> using big_vector=std::vector<T,big_alloc<T>>; template<typename T> using big_basic_string=std::basic_string<T,std::char_traits<T>,big_alloc<T>>; template<typename T> using big_deque=std::deque<T,big_alloc<T>>; template<typename T> using big_stack=std::stack<T,big_deque<T>>; template<typename T> using big_queue=std::queue<T,big_deque<T>>; template<typename T> using big_priority_queue=std::priority_queue<T,big_vector<T>>; template<typename T> using big_forward_list=std::forward_list<T,big_alloc<T>>; using big_string=big_basic_string<char>; template<typename Key,typename Value,typename Compare=std::less<Key>> using big_map=std::map<Key,Value,Compare,big_alloc<std::pair<const Key,Value>>>; template<typename T,typename Compare=std::less<T>> using big_multiset=std::multiset<T,Compare,big_alloc<T>>; template<typename T,typename Compare=std::less<T>> using big_set=std::set<T,Compare,big_alloc<T>>; }
#line 6 "cp-algo/math/combinatorics.hpp"
#include <ranges>
namespace cp_algo::math{ template<typename T> T fact(auto n){ static const auto F= []{ big_vector<T>F(maxn); F[0] =T(1); for(int i=1;i<maxn;i++){ F[i] =F[i-1] *T(i); } return F; }(); return F[n]; } template<typename T> T rfact(auto n){ static const auto F= []{ big_vector<T>F(maxn); int t=(int)std::min<int64_t>(T::mod(),maxn)-1; F[t] =T(1)/fact<T>(t); for(int i=t-1;i>=0;i--){ F[i] =F[i+1] *T(i+1); } return F; }(); return F[n]; } template<typename T,int base> T pow_fixed(int n){ static const auto prec= []{ big_vector<T>prec_low(1<<16),prec_high(1<<16); prec_low[0] =prec_high[0] =T(1); T step_low=T(base); T step_high=bpow(T(base),1<<16); for(int i=1;i<(1<<16);i++){ prec_low[i] =prec_low[i-1] *step_low; prec_high[i] =prec_high[i-1] *step_high; } return std::pair{std::move(prec_low),std::move(prec_high)}; }(); return prec.first[n&0xFFFF] *prec.second[n>>16]; } template<typename T> big_vector<T>bulk_invs(auto const&args){ big_vector<T>res(std::size(args),args[0]); for(size_t i=1;i<std::size(args);i++){ res[i] =res[i-1] *args[i]; } auto all_invs=T(1)/res.back(); for(size_t i=std::size(args)-1;i>0;i--){ res[i] =all_invs*res[i-1]; all_invs*=args[i]; } res[0] =all_invs; return res; } template<typename T> T small_inv(auto n){ static auto F=bulk_invs<T>(std::views::iota(1,maxn)); return F[n-1]; } template<typename T> T binom_large(T n,auto r){ assert(r<maxn); T ans=1; for(decltype(r)i=0;i<r;i++){ ans=ans*T(n-i)*small_inv<T>(i+1); } return ans; } template<typename T> T binom(auto n,auto r){ if(r<0||r>n){ return T(0); }else if(n>=maxn){ return binom_large(T(n),r); }else{ return fact<T>(n)*rfact<T>(r)*rfact<T>(n-r); } } }
Back to top page