This documentation is automatically generated by online-judge-tools/verification-helper
#include "algorithm/Math/Combinatorics/naive_combination.hpp"順列,組合せ,重複組合せの総数を $\mathcal{O}(K)$ で数え上げる.
#ifndef ALGORITHM_NAIVE_COMBINATION_HPP
#define ALGORITHM_NAIVE_COMBINATION_HPP 1
#include <algorithm>
#include <cassert>
#include "../ModularArithmetic/mod_inv.hpp"
namespace algorithm {
// 順列.O(K).
constexpr long long nPk(long long n, long long k) {
assert(n >= 0);
assert(k >= 0);
if(n < k) return 0;
long long res = 1;
for(long long i = 0; i < k; ++i) res *= (n - i);
return res;
}
// 順列(mod付き).O(K).
constexpr long long nPk(long long n, long long k, int mod) {
assert(n >= 0);
assert(k >= 0);
assert(mod >= 1);
if(n < k) return 0;
n %= mod;
long long res = 1 % mod;
for(long long i = 0; i < k; ++i) {
long long tmp = n - i;
if(tmp < 0) tmp += mod;
res = res * tmp % mod;
}
return res;
}
// 組合せ.O(min(K,N-K)).
constexpr long long nCk(long long n, long long k) {
assert(n >= 0);
assert(k >= 0);
if(n < k) return 0;
k = std::min(k, n - k);
long long res = 1;
for(long long i = 0; i < k; ++i) res = res * (n - i) / (i + 1);
return res;
}
// 組合せ(mod付き).
constexpr long long nCk(long long n, long long k, int mod) {
assert(n >= 0);
assert(k >= 0);
assert(mod >= 1);
if(n < k) return 0;
k = std::min(k, n - k);
return nPk(n, k, mod) * mod_inv(nPk(k, k, mod), mod) % mod;
}
// 重複組合せ.O(min(N-1,K)).
constexpr long long nHk(long long n, long long k) {
assert(n >= 0);
assert(k >= 0);
if(k == 0) return 1;
if(n == 0) return 0;
return nCk(k + n - 1, k);
}
// 重複組合せ(mod付き).
constexpr long long nHk(long long n, long long k, int mod) {
assert(n >= 0);
assert(k >= 0);
assert(mod >= 1);
if(k == 0) return 1 % mod;
if(n == 0) return 0;
return nCk(k + n - 1, k, mod);
}
} // namespace algorithm
#endif#line 1 "algorithm/Math/Combinatorics/naive_combination.hpp"
#include <algorithm>
#include <cassert>
#line 1 "algorithm/Math/ModularArithmetic/mod_inv.hpp"
#line 5 "algorithm/Math/ModularArithmetic/mod_inv.hpp"
#include <concepts>
#include <cstdint>
#include <utility>
#line 1 "algorithm/Math/ModularArithmetic/modulo.hpp"
#line 6 "algorithm/Math/ModularArithmetic/modulo.hpp"
namespace algorithm {
namespace internal {
// return x mod m.
template <std::unsigned_integral Type>
constexpr std::uint32_t modulo(Type x, std::uint32_t mod) { return x % mod; }
// return x mod m.
template <std::unsigned_integral Type>
constexpr std::uint32_t modulo(Type x, std::int32_t mod) { return modulo(x, static_cast<std::uint32_t>(mod)); }
// return x mod m.
template <std::signed_integral Type>
constexpr std::uint32_t modulo(Type x, std::uint32_t mod) {
x %= static_cast<std::int64_t>(mod);
if(x < 0) x += static_cast<std::int64_t>(mod);
return x;
}
// return x mod m.
template <std::signed_integral Type>
constexpr std::uint32_t modulo(Type x, std::int32_t mod) {
x %= mod;
if(x < 0) x += mod;
return x;
}
} // namespace internal
} // namespace algorithm
#line 10 "algorithm/Math/ModularArithmetic/mod_inv.hpp"
namespace algorithm {
namespace internal {
// return pair of (x, g) s.t. g=gcd(a,m), ax=g (mod m), 0<=x<m/g.
constexpr std::pair<std::uint32_t, std::uint32_t> mod_inv(std::uint32_t a, std::uint32_t m) {
if(a == 0) return {0, m};
std::uint32_t s = m, t = a;
std::uint32_t u = m, v = 1;
while(true) {
std::uint32_t q = s / t;
s -= t * q, u -= v * q;
if(s == 0) return {v, t};
q = t / s;
t -= s * q, v += (m - u) * q;
if(t == 0) return {u, s};
}
}
} // namespace internal
// モジュラ逆数(乗法逆元).
// a^-1 mod m を求める.解が存在する必要十分条件は,aとmが互いに素であること.O(log a).
template <std::integral Type>
constexpr std::int64_t mod_inv(Type a, std::int32_t m) {
assert(m >= 1);
auto [x, g] = internal::mod_inv(::algorithm::internal::modulo(a, m), m);
assert(g == 1);
return x;
}
} // namespace algorithm
#line 8 "algorithm/Math/Combinatorics/naive_combination.hpp"
namespace algorithm {
// 順列.O(K).
constexpr long long nPk(long long n, long long k) {
assert(n >= 0);
assert(k >= 0);
if(n < k) return 0;
long long res = 1;
for(long long i = 0; i < k; ++i) res *= (n - i);
return res;
}
// 順列(mod付き).O(K).
constexpr long long nPk(long long n, long long k, int mod) {
assert(n >= 0);
assert(k >= 0);
assert(mod >= 1);
if(n < k) return 0;
n %= mod;
long long res = 1 % mod;
for(long long i = 0; i < k; ++i) {
long long tmp = n - i;
if(tmp < 0) tmp += mod;
res = res * tmp % mod;
}
return res;
}
// 組合せ.O(min(K,N-K)).
constexpr long long nCk(long long n, long long k) {
assert(n >= 0);
assert(k >= 0);
if(n < k) return 0;
k = std::min(k, n - k);
long long res = 1;
for(long long i = 0; i < k; ++i) res = res * (n - i) / (i + 1);
return res;
}
// 組合せ(mod付き).
constexpr long long nCk(long long n, long long k, int mod) {
assert(n >= 0);
assert(k >= 0);
assert(mod >= 1);
if(n < k) return 0;
k = std::min(k, n - k);
return nPk(n, k, mod) * mod_inv(nPk(k, k, mod), mod) % mod;
}
// 重複組合せ.O(min(N-1,K)).
constexpr long long nHk(long long n, long long k) {
assert(n >= 0);
assert(k >= 0);
if(k == 0) return 1;
if(n == 0) return 0;
return nCk(k + n - 1, k);
}
// 重複組合せ(mod付き).
constexpr long long nHk(long long n, long long k, int mod) {
assert(n >= 0);
assert(k >= 0);
assert(mod >= 1);
if(k == 0) return 1 % mod;
if(n == 0) return 0;
return nCk(k + n - 1, k, mod);
}
} // namespace algorithm