競技プログラミング用 C++ ライブラリ
math/fixed_size_subset_sum.hpp · GitHub で見る
共通ページ
非負整数の集合から異なる要素をちょうど k 個選び、作れる和をすべて求めます。選び方の個数ではなく、和の集合を返します。
#include "lib/math/fixed_size_subset_sum.hpp"
#include <iostream>
int main() {
std::vector<int> values{0, 2, 5, 8};
auto sums = fixed_size_subset_sums(values, 2);
for (std::size_t i = 0; i < sums.size(); ++i)
std::cout << (i ? " " : "") << sums[i];
std::cout << '\n';
}
出力は 2 5 7 8 10 13 です。返り値は昇順なので、std::binary_search(sums.begin(), sums.end(), x) で和 x を作れるか調べられます。
| 操作 | 内容・返り値 | 計算量 |
|---|---|---|
fixed_size_subset_sums(values,k) |
std::vector<int> と非負の int をこの順に渡す。作れる和を昇順・重複なしの std::vector<long long> で返す。既定引数なし |
NTT 経路は \(O(M\log M+PL(\log L+K^2)+R)\) |
values の要素は 0 以上 INT_MAX 以下です。入力の順序は自由で、入力元の配列は変更しません。重複した値は一つにまとめます。同じ値を複数回使いたい場合の多重集合には対応しません。入力の長さは INT_MAX 以下にします。
k = 0 なら空集合を選んだ和として {0} を返します。k が異なる値の個数を超える場合は空配列です。空入力でもこの規則を使います。値 0 も一つの要素として選べます。和が 32 bit を超える場合も long long で返します。
異なる値の個数を \(m\)、\(K=\min(k,m-k)\) とします。選ぶ個数が多い場合は、選ばない \(K\) 個の和を全体の和から引きます。\(K=0,1\)、組合せ数が 32768 以下、または値が等差数列をなす場合は、NTT を使わずに求めます。
それ以外は、最小値を引き、全要素の差の最大公約数で割って座標を圧縮します。この座標で上位 \(K\) 個の和を \(H\)、\(L\) を \(H+1\) 以上の最小の 2 冪とします。\(H<2^{23}\) が必要です。また、係数の上限 \(\displaystyle B=\left\lfloor\frac{\binom m{K-1}}K\right\rfloor\) が、内部部品で使う 10 個の NTT 素数の積未満である必要があります。NTT が必要な入力でどちらかを満たさない場合は std::length_error を送出します。例えば値が 0 から 200000 の範囲で k <= 16 なら、両条件を満たします。
有限体での数え上げには Newton の恒等式を使います。一つでも非ゼロの余りを持つ和は必ず作れます。余りがすべて 0 の和は、係数の上限と使用した素数の積を比較して判定します。必要に応じて素数を追加するため、乱数による誤判定や、係数が法の倍数になることによる取りこぼしはありません。
入力の長さを \(M\)、出力の長さを \(R\)、使用した素数の個数を \(P\leq10\) とすると、NTT 経路は \(O(M\log M+PL(\log L+K^2)+R)\) 時間、\(O(M+L+K+R)\) 空間です。各素数につき順変換と逆変換を一度ずつ行います。組合せ数 \(E=\binom mK\) が 32768 以下なら \(O(M\log M+KE+E\log E)\) 時間、\(O(M+K+E)\) 空間です。等差数列なら \(O(M\log M+R)\) 時間、\(O(M+R)\) 空間です。k = 0 は入力の検査のみで \(O(M)\) 時間です。
#ifndef FIXED_SIZE_SUBSET_SUM_HPP
#define FIXED_SIZE_SUBSET_SUM_HPP
#include "../components/fixed_size_subset_sum.hpp"
#include <limits>
#include <numeric>
inline std::vector<long long> fixed_size_subset_sums(std::vector<int> values, int k) {
assert(k >= 0 && values.size() <= std::size_t(std::numeric_limits<int>::max()));
for ([[maybe_unused]] int x : values) assert(x >= 0);
if (!k) return {0};
std::sort(values.begin(), values.end());
values.erase(std::unique(values.begin(), values.end()), values.end());
int count = int(values.size());
if (k > count) return {};
long long total = std::accumulate(values.begin(), values.end(), 0LL);
bool complement = k > count - k;
if (complement) k = count - k;
if (!k) return {total};
if (k == 1) {
std::vector<long long> result(values.begin(), values.end());
if (complement) {
for (auto& x : result) x = total - x;
std::reverse(result.begin(), result.end());
}
return result;
}
std::uint64_t ways = 1;
for (int i = 1; i <= k && ways <= 32768; ++i) ways = ways * (count - i + 1) / i;
std::vector<long long> result;
if (ways <= 32768) {
result.reserve(ways);
auto visit = [&](auto&& self, int start, int left, long long sum) -> void {
if (!left) { result.push_back(sum); return; }
for (int i = start; i <= count - left; ++i) self(self, i + 1, left - 1, sum + values[i]);
};
visit(visit, 0, k, 0);
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());
} else {
int base = values[0], step = 0;
for (int x : values) step = std::gcd(step, x - base);
for (auto& x : values) x = (x - base) / step;
long long low = 0, high = 0;
for (int i = 0; i < k; ++i) low += values[i], high += values[count - 1 - i];
if (values.back() == count - 1) {
result.reserve(std::size_t(high - low) + 1);
for (long long x = low; x <= high; ++x) result.push_back(1LL * k * base + x * step);
} else {
if (high >= (1 << 23)) throw std::length_error("sum range is too large");
auto bound = fixed_size_subset_sum_detail::coefficient_bound(count, k);
std::vector<unsigned char> present(std::size_t(high - low) + 1);
std::size_t missing = present.size();
fixed_size_subset_sum_detail::Digits product{1};
for (auto pass : fixed_size_subset_sum_detail::passes) {
missing -= pass.include(values, k, int(low), present);
fixed_size_subset_sum_detail::multiply(product, pass.modulus);
if (!missing || fixed_size_subset_sum_detail::less(bound, product)) break;
int left = 0, right = 0, candidates = 0;
long long small = low - values[k - 1], large = high - values[count - k];
for (std::size_t i = 0; i < present.size() && candidates < count; ++i) {
if (present[i]) continue;
long long sum = low + i;
while (left < count && values[left] < sum - large) ++left;
while (right < count && values[right] <= sum - small) ++right;
candidates = std::max(candidates, right - left);
}
bound = fixed_size_subset_sum_detail::coefficient_bound(candidates, k);
if (fixed_size_subset_sum_detail::less(bound, product)) break;
}
result.reserve(present.size() - missing);
for (std::size_t i = 0; i < present.size(); ++i)
if (present[i]) result.push_back(1LL * k * base + (low + i) * step);
}
}
if (complement) {
for (auto& x : result) x = total - x;
std::reverse(result.begin(), result.end());
}
return result;
}
#endif
#line 1 "math/fixed_size_subset_sum.hpp"
#line 1 "components/fixed_size_subset_sum.hpp"
#line 1 "components/ntt_mod.hpp"
#include <algorithm>
#include <bit>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <vector>
#if defined(__x86_64__) || defined(__i386__)
#include <immintrin.h>
#endif
namespace ntt_mod_detail {
template <std::uint32_t mod> struct Vector;
template <std::uint32_t mod>
std::uint32_t power(std::uint32_t a, std::uint32_t k) {
std::uint32_t result = 1;
for (; k; k >>= 1, a = std::uint64_t(a) * a % mod)
if (k & 1) result = std::uint64_t(result) * a % mod;
return result;
}
template <std::uint32_t mod>
void butterfly(std::uint32_t *a, std::size_t n, std::size_t half,
const std::uint32_t *roots, const std::uint32_t *quotients) {
for (std::size_t start = 0; start < n; start += 2 * half)
for (std::size_t j = 0; j < half; ++j) {
std::uint32_t x = a[start + j], v = a[start + j + half];
std::uint32_t q = std::uint64_t(v) * quotients[j] >> 32;
std::uint32_t y = v * roots[j] - q * mod;
if (y >= mod) y -= mod;
a[start + j] = x + y < mod ? x + y : x + y - mod;
a[start + j + half] = x >= y ? x - y : x + mod - y;
}
}
#if defined(__x86_64__) || defined(__i386__)
template <std::uint32_t mod>
__attribute__((target("avx2")))
void butterfly_avx2(std::uint32_t *a, std::size_t n, std::size_t half,
const std::uint32_t *roots, const std::uint32_t *quotients) {
using V = Vector<mod>;
for (std::size_t start = 0; start < n; start += 2 * half)
for (std::size_t j = 0; j < half; j += 8) {
auto x = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + start + j));
auto y = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + start + j + half));
auto r = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(roots + j));
auto q = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(quotients + j));
y = V::multiply(y, r, q);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + start + j), V::add(x, y));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + start + j + half), V::subtract(x, y));
}
}
#endif
template <std::uint32_t mod>
std::uint32_t shoup(std::uint32_t value, std::uint32_t factor, std::uint32_t quotient) {
std::uint32_t q = std::uint64_t(value) * quotient >> 32;
std::uint32_t result = value * factor - q * mod;
return result < mod ? result : result - mod;
}
template <std::uint32_t mod>
void forward_stage(std::uint32_t *a, std::size_t n, std::size_t half,
const std::uint32_t *roots, const std::uint32_t *quotients) {
for (std::size_t start = 0; start < n; start += 2 * half)
for (std::size_t j = 0; j < half; ++j) {
std::uint32_t x = a[start + j], y = a[start + j + half];
a[start + j] = x + y < mod ? x + y : x + y - mod;
std::uint32_t difference = x >= y ? x - y : x + mod - y;
a[start + j + half] = shoup<mod>(difference, roots[j], quotients[j]);
}
}
#if defined(__x86_64__) || defined(__i386__)
template <std::uint32_t mod>
__attribute__((target("avx2")))
void forward_stage_avx2(std::uint32_t *a, std::size_t n, std::size_t half,
const std::uint32_t *roots, const std::uint32_t *quotients) {
using V = Vector<mod>;
for (std::size_t start = 0; start < n; start += 2 * half)
for (std::size_t j = 0; j < half; j += 8) {
auto x = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + start + j));
auto y = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + start + j + half));
auto r = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(roots + j));
auto q = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(quotients + j));
auto difference = V::multiply(V::subtract(x, y), r, q);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + start + j), V::add(x, y));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + start + j + half), difference);
}
}
#endif
#if defined(__x86_64__) || defined(__i386__)
template <std::uint32_t mod>
struct Vector {
static constexpr std::uint32_t range = mod < (std::uint32_t(1) << 30) ? 2 * mod : mod;
__attribute__((target("avx2"), always_inline))
static inline __m256i add(__m256i x, __m256i y) {
auto sum = _mm256_add_epi32(x, y);
return _mm256_min_epu32(sum, _mm256_sub_epi32(sum, _mm256_set1_epi32(range)));
}
__attribute__((target("avx2"), always_inline))
static inline __m256i subtract(__m256i x, __m256i y) {
auto difference = _mm256_sub_epi32(x, y);
return _mm256_min_epu32(difference, _mm256_add_epi32(difference, _mm256_set1_epi32(range)));
}
__attribute__((target("avx2"), always_inline))
static inline __m256i multiply(__m256i x, __m256i factor, __m256i quotient) {
auto even = _mm256_mul_epu32(x, quotient);
auto odd = _mm256_mul_epu32(_mm256_srli_epi64(x, 32), _mm256_srli_epi64(quotient, 32));
auto q = _mm256_blend_epi32(_mm256_srli_epi64(even, 32), odd, 0xaa);
auto result = _mm256_sub_epi32(_mm256_mullo_epi32(x, factor), _mm256_mullo_epi32(q, _mm256_set1_epi32(mod)));
if constexpr (range == 2 * mod) return result;
return _mm256_min_epu32(result, _mm256_sub_epi32(result, _mm256_set1_epi32(mod)));
}
};
template <std::uint32_t mod, bool inverse>
__attribute__((target("avx2")))
void leaf_avx2(std::uint32_t *a, std::size_t n, const std::uint32_t *roots, const std::uint32_t *quotients) {
using V = Vector<mod>;
auto r4 = _mm256_broadcastsi128_si256(_mm_loadu_si128(reinterpret_cast<const __m128i *>(roots + 4)));
auto q4 = _mm256_broadcastsi128_si256(_mm_loadu_si128(reinterpret_cast<const __m128i *>(quotients + 4)));
auto r2 = _mm256_setr_epi32(roots[2], roots[3], roots[2], roots[3], roots[2], roots[3], roots[2], roots[3]);
auto q2 = _mm256_setr_epi32(quotients[2], quotients[3], quotients[2], quotients[3], quotients[2], quotients[3], quotients[2], quotients[3]);
for (std::size_t i = 0; i < n; i += 8) {
auto x = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + i));
if constexpr (!inverse) {
auto y = _mm256_permute2x128_si256(x, x, 1);
x = _mm256_blend_epi32(V::add(x, y), V::multiply(V::subtract(y, x), r4, q4), 0xf0);
y = _mm256_shuffle_epi32(x, 0x4e);
x = _mm256_blend_epi32(V::add(x, y), V::multiply(V::subtract(y, x), r2, q2), 0xcc);
y = _mm256_shuffle_epi32(x, 0xb1);
x = _mm256_blend_epi32(V::add(x, y), V::subtract(y, x), 0xaa);
} else {
auto y = _mm256_shuffle_epi32(x, 0xb1);
x = _mm256_blend_epi32(V::add(x, y), V::subtract(y, x), 0xaa);
x = _mm256_blend_epi32(x, V::multiply(x, r2, q2), 0xcc);
y = _mm256_shuffle_epi32(x, 0x4e);
x = _mm256_blend_epi32(V::add(x, y), V::subtract(y, x), 0xcc);
x = _mm256_blend_epi32(x, V::multiply(x, r4, q4), 0xf0);
y = _mm256_permute2x128_si256(x, x, 1);
x = _mm256_blend_epi32(V::add(x, y), V::subtract(y, x), 0xf0);
}
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + i), x);
}
}
template <std::uint32_t mod, bool inverse>
__attribute__((target("avx2")))
void pair_avx2(std::uint32_t *a, std::size_t n, std::size_t h, const std::uint32_t *roots, const std::uint32_t *quotients) {
using V = Vector<mod>;
for (std::size_t start = 0; start < n; start += 4 * h)
for (std::size_t j = 0; j < h; j += 8) {
auto x0 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + start + j));
auto x1 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + start + j + h));
auto x2 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + start + j + 2 * h));
auto x3 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + start + j + 3 * h));
auto r0 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(roots + 2 * h + j));
auto q0 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(quotients + 2 * h + j));
auto r1 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(roots + 3 * h + j));
auto q1 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(quotients + 3 * h + j));
auto r2 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(roots + h + j));
auto q2 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(quotients + h + j));
if constexpr (!inverse) {
auto y0 = V::add(x0, x2), y1 = V::add(x1, x3);
auto y2 = V::multiply(V::subtract(x0, x2), r0, q0);
auto y3 = V::multiply(V::subtract(x1, x3), r1, q1);
x0 = V::add(y0, y1); x1 = V::multiply(V::subtract(y0, y1), r2, q2);
x2 = V::add(y2, y3); x3 = V::multiply(V::subtract(y2, y3), r2, q2);
} else {
x1 = V::multiply(x1, r2, q2); x3 = V::multiply(x3, r2, q2);
auto y0 = V::add(x0, x1), y1 = V::subtract(x0, x1);
auto y2 = V::multiply(V::add(x2, x3), r0, q0);
auto y3 = V::multiply(V::subtract(x2, x3), r1, q1);
x0 = V::add(y0, y2); x2 = V::subtract(y0, y2);
x1 = V::add(y1, y3); x3 = V::subtract(y1, y3);
}
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + start + j), x0);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + start + j + h), x1);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + start + j + 2 * h), x2);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + start + j + 3 * h), x3);
}
}
#endif
#if defined(__x86_64__) || defined(__i386__)
template <std::uint32_t mod>
__attribute__((target("avx2")))
void build_roots_avx2(std::uint32_t *roots, std::uint32_t *quotients, std::size_t half, std::uint32_t step) {
std::uint32_t initial[8], w = 1;
for (int i = 0; i < 8; ++i) {
initial[i] = w;
w = std::uint64_t(w) * step % mod;
}
auto x = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(initial));
auto factor = _mm256_set1_epi32(w), quotient = _mm256_set1_epi32((std::uint64_t(w) << 32) / mod);
for (std::size_t j = 0; j < half; j += 8) {
x = _mm256_min_epu32(x, _mm256_sub_epi32(x, _mm256_set1_epi32(mod)));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(roots + j), x);
for (int k = 0; k < 8; ++k) quotients[j + k] = (std::uint64_t(roots[j + k]) << 32) / mod;
x = Vector<mod>::multiply(x, factor, quotient);
}
}
#endif
template <std::uint32_t mod, std::uint32_t root>
struct Plan {
std::vector<std::uint32_t> roots, quotients;
explicit Plan(std::size_t n, [[maybe_unused]] bool vectorized = false) : roots(n), quotients(n) {
static_assert(mod > 2 && mod < (std::uint32_t(1) << 31));
assert(std::has_single_bit(n) && (mod - 1) % n == 0);
if (n == 1) return;
std::size_t half = n / 2;
std::uint32_t step = power<mod>(root, (mod - 1) / n), w = 1;
#if defined(__x86_64__) || defined(__i386__)
if (vectorized && half >= 8) build_roots_avx2<mod>(roots.data() + half, quotients.data() + half, half, step);
else
#endif
for (std::size_t j = 0; j < half; ++j) {
roots[half + j] = w;
quotients[half + j] = (std::uint64_t(w) << 32) / mod;
w = std::uint64_t(w) * step % mod;
}
for (half /= 2; half; half /= 2)
for (std::size_t j = 0; j < half; ++j) {
roots[half + j] = roots[2 * half + 2 * j];
quotients[half + j] = quotients[2 * half + 2 * j];
}
}
};
template <std::uint32_t mod, std::uint32_t root>
void forward(std::vector<std::uint32_t> &a, const Plan<mod, root> &plan, [[maybe_unused]] bool vectorized) {
std::size_t half = a.size() / 2;
#if defined(__x86_64__) || defined(__i386__)
if (vectorized && a.size() >= 8) {
for (; half >= 16; half /= 4) pair_avx2<mod, false>(a.data(), a.size(), half / 2, plan.roots.data(), plan.quotients.data());
if (half == 8) forward_stage_avx2<mod>(a.data(), a.size(), half, plan.roots.data() + half, plan.quotients.data() + half);
leaf_avx2<mod, false>(a.data(), a.size(), plan.roots.data(), plan.quotients.data());
return;
}
#endif
for (; half; half /= 2) forward_stage<mod>(a.data(), a.size(), half, plan.roots.data() + half, plan.quotients.data() + half);
}
template <std::uint32_t mod, std::uint32_t root>
void backward(std::vector<std::uint32_t> &a, const Plan<mod, root> &plan, [[maybe_unused]] bool vectorized) {
std::size_t half = 1;
#if defined(__x86_64__) || defined(__i386__)
if (vectorized && a.size() >= 8) {
leaf_avx2<mod, true>(a.data(), a.size(), plan.roots.data(), plan.quotients.data());
half = 8;
for (; 2 * half < a.size(); half *= 4) pair_avx2<mod, true>(a.data(), a.size(), half, plan.roots.data(), plan.quotients.data());
if (half < a.size()) butterfly_avx2<mod>(a.data(), a.size(), half, plan.roots.data() + half, plan.quotients.data() + half);
return;
}
#endif
for (; half < a.size(); half *= 2) butterfly<mod>(a.data(), a.size(), half, plan.roots.data() + half, plan.quotients.data() + half);
}
#if defined(__x86_64__) || defined(__i386__)
template <std::uint32_t mod>
__attribute__((target("avx2")))
void scale_avx2(std::uint32_t *a, std::size_t n, std::uint32_t factor, std::uint32_t quotient) {
auto r = _mm256_set1_epi32(factor), q = _mm256_set1_epi32(quotient);
for (std::size_t i = 0; i < n; i += 8) {
auto x = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + i));
auto y = Vector<mod>::multiply(x, r, q);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + i), _mm256_min_epu32(y, _mm256_sub_epi32(y, _mm256_set1_epi32(mod))));
}
}
#endif
template <std::uint32_t mod>
void scale(std::vector<std::uint32_t> &a, std::uint32_t factor, [[maybe_unused]] bool vectorized) {
std::uint32_t quotient = (std::uint64_t(factor) << 32) / mod;
#if defined(__x86_64__) || defined(__i386__)
if (vectorized && a.size() >= 8) {
scale_avx2<mod>(a.data(), a.size(), factor, quotient);
return;
}
#endif
for (auto &x : a) x = shoup<mod>(x, factor, quotient);
}
inline void bit_reverse(std::vector<std::uint32_t> &a) {
for (std::size_t i = 1, j = 0; i < a.size(); ++i) {
std::size_t bit = a.size() >> 1;
for (; j & bit; bit >>= 1) j ^= bit;
j ^= bit;
if (i < j) std::swap(a[i], a[j]);
}
}
template <std::uint32_t mod, std::uint32_t root>
void transform(std::vector<std::uint32_t> &a, bool inverse, bool vectorized) {
Plan<mod, root> plan(a.size(), vectorized);
if (inverse) {
bit_reverse(a);
backward(a, plan, vectorized);
std::reverse(a.begin() + 1, a.end());
scale<mod>(a, mod - (mod - 1) / a.size(), vectorized);
} else {
forward(a, plan, vectorized);
if constexpr (mod < (std::uint32_t(1) << 30)) for (auto &x : a) if (x >= mod) x -= mod;
bit_reverse(a);
}
}
template <std::uint32_t mod>
constexpr std::uint32_t negative_inverse = [] {
std::uint32_t value = mod;
for (int i = 0; i < 5; ++i) value *= 2 - mod * value;
return 0 - value;
}();
#if defined(__x86_64__) || defined(__i386__)
template <std::uint32_t mod>
__attribute__((target("avx2")))
void pointwise_avx2(std::uint32_t *a, const std::uint32_t *b, std::size_t n) {
auto p = _mm256_set1_epi32(mod), inverse = _mm256_set1_epi32(negative_inverse<mod>);
for (std::size_t i = 0; i < n; i += 8) {
auto x = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + i));
auto y = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(b + i));
auto even = _mm256_mul_epu32(x, y);
auto odd = _mm256_mul_epu32(_mm256_srli_epi64(x, 32), _mm256_srli_epi64(y, 32));
auto qe = _mm256_mul_epu32(even, inverse), qo = _mm256_mul_epu32(odd, inverse);
even = _mm256_srli_epi64(_mm256_add_epi64(even, _mm256_mul_epu32(qe, p)), 32);
odd = _mm256_add_epi64(odd, _mm256_mul_epu32(qo, p));
x = _mm256_blend_epi32(even, odd, 0xaa);
x = _mm256_min_epu32(x, _mm256_sub_epi32(x, p));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + i), x);
}
}
#endif
template <std::uint32_t mod>
void pointwise(std::vector<std::uint32_t> &a, const std::vector<std::uint32_t> &b, [[maybe_unused]] bool vectorized) {
#if defined(__x86_64__) || defined(__i386__)
if (vectorized && a.size() >= 8) {
pointwise_avx2<mod>(a.data(), b.data(), a.size());
return;
}
#endif
for (std::size_t i = 0; i < a.size(); ++i) {
std::uint64_t product = std::uint64_t(a[i]) * b[i];
std::uint32_t q = std::uint32_t(product) * negative_inverse<mod>;
std::uint32_t value = (product + std::uint64_t(q) * mod) >> 32;
a[i] = value < mod ? value : value - mod;
}
}
}
template <std::uint32_t mod, std::uint32_t root>
void ntt_mod(std::vector<std::uint32_t> &a, bool inverse = false) {
bool vectorized = false;
#if defined(__x86_64__) || defined(__i386__)
vectorized = __builtin_cpu_supports("avx2");
#endif
ntt_mod_detail::transform<mod, root>(a, inverse, vectorized);
}
#line 5 "components/fixed_size_subset_sum.hpp"
#include <array>
#include <stdexcept>
namespace fixed_size_subset_sum_detail {
using Word = std::uint32_t;
using Digits = std::vector<Word>;
inline void multiply(Digits& a, Word x) {
std::uint64_t carry = 0;
for (auto& v : a) {
carry += std::uint64_t(v) * x;
v = Word(carry);
carry >>= 32;
}
if (carry) a.push_back(Word(carry));
}
inline void divide(Digits& a, Word x) {
std::uint64_t carry = 0;
for (std::size_t i = a.size(); i--;) {
carry = (carry << 32) | a[i];
a[i] = Word(carry / x);
carry %= x;
}
while (a.size() > 1 && !a.back()) a.pop_back();
}
inline bool less(const Digits& a, const Digits& b) {
if (a.size() != b.size()) return a.size() < b.size();
return std::lexicographical_compare(a.rbegin(), a.rend(), b.rbegin(), b.rend());
}
template<Word Mod, Word Root>
std::size_t include_modular(const std::vector<int>& values, int k, int low, std::vector<unsigned char>& present) {
std::size_t size = std::bit_ceil(std::size_t(low) + present.size());
bool simd = false;
#if defined(__x86_64__) || defined(__i386__)
simd = __builtin_cpu_supports("avx2");
#endif
ntt_mod_detail::Plan<Mod, Root> plan(size, simd);
std::vector<Word> frequency(size), result(size), powers(k + 1), elementary(k + 1), inverse(k + 1, 1);
for (int x : values) frequency[x] = 1;
ntt_mod_detail::forward(frequency, plan, simd);
if constexpr (Mod < (Word(1) << 30)) for (auto& x : frequency) if (x >= Mod) x -= Mod;
ntt_mod_detail::bit_reverse(frequency);
for (int i = 2; i <= k; ++i) inverse[i] = Mod - std::uint64_t(Mod / i) * inverse[Mod % i] % Mod;
elementary[0] = 1;
for (std::size_t x = 0; x < size; ++x) {
for (int j = 1; j <= k; ++j) {
Word value = frequency[(x * j) & (size - 1)];
powers[j] = (j & 1) || !value ? value : Mod - value;
}
elementary[1] = powers[1];
for (int i = 2; i <= k; ++i) {
__uint128_t sum = powers[i];
for (int j = 1; j < i; ++j) sum += std::uint64_t(elementary[i - j]) * powers[j];
elementary[i] = std::uint64_t(sum % Mod) * inverse[i] % Mod;
}
result[x] = elementary[k];
}
ntt_mod_detail::bit_reverse(result);
ntt_mod_detail::backward(result, plan, simd);
std::reverse(result.begin() + 1, result.end());
std::size_t added = 0;
for (std::size_t i = 0; i < present.size(); ++i) {
Word x = result[low + i];
bool nonzero = x && x != Mod;
added += !present[i] && nonzero;
present[i] |= nonzero;
}
return added;
}
struct Pass {
Word modulus;
std::size_t (*include)(const std::vector<int>&, int, int, std::vector<unsigned char>&);
};
inline constexpr std::array passes{
Pass{998244353, include_modular<998244353, 3>},
Pass{2130706433, include_modular<2130706433, 3>},
Pass{2113929217, include_modular<2113929217, 5>},
Pass{2088763393, include_modular<2088763393, 5>},
Pass{2013265921, include_modular<2013265921, 31>},
Pass{1811939329, include_modular<1811939329, 13>},
Pass{1711276033, include_modular<1711276033, 29>},
Pass{1484783617, include_modular<1484783617, 5>},
Pass{1300234241, include_modular<1300234241, 3>},
Pass{1224736769, include_modular<1224736769, 3>}
};
inline Digits coefficient_bound(int n, int k) {
if (n < k) return {0};
Digits bound{1}, limit{1};
for (auto pass : passes) multiply(limit, pass.modulus);
multiply(limit, Word(k));
int degree = std::min(k - 1, n - k + 1);
for (int i = 1; i <= degree; ++i) {
multiply(bound, Word(n - i + 1));
divide(bound, Word(i));
if (!less(bound, limit)) throw std::length_error("too many subsets");
}
divide(bound, Word(k));
return bound;
}
}
#line 5 "math/fixed_size_subset_sum.hpp"
#include <limits>
#include <numeric>
inline std::vector<long long> fixed_size_subset_sums(std::vector<int> values, int k) {
assert(k >= 0 && values.size() <= std::size_t(std::numeric_limits<int>::max()));
for ([[maybe_unused]] int x : values) assert(x >= 0);
if (!k) return {0};
std::sort(values.begin(), values.end());
values.erase(std::unique(values.begin(), values.end()), values.end());
int count = int(values.size());
if (k > count) return {};
long long total = std::accumulate(values.begin(), values.end(), 0LL);
bool complement = k > count - k;
if (complement) k = count - k;
if (!k) return {total};
if (k == 1) {
std::vector<long long> result(values.begin(), values.end());
if (complement) {
for (auto& x : result) x = total - x;
std::reverse(result.begin(), result.end());
}
return result;
}
std::uint64_t ways = 1;
for (int i = 1; i <= k && ways <= 32768; ++i) ways = ways * (count - i + 1) / i;
std::vector<long long> result;
if (ways <= 32768) {
result.reserve(ways);
auto visit = [&](auto&& self, int start, int left, long long sum) -> void {
if (!left) { result.push_back(sum); return; }
for (int i = start; i <= count - left; ++i) self(self, i + 1, left - 1, sum + values[i]);
};
visit(visit, 0, k, 0);
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());
} else {
int base = values[0], step = 0;
for (int x : values) step = std::gcd(step, x - base);
for (auto& x : values) x = (x - base) / step;
long long low = 0, high = 0;
for (int i = 0; i < k; ++i) low += values[i], high += values[count - 1 - i];
if (values.back() == count - 1) {
result.reserve(std::size_t(high - low) + 1);
for (long long x = low; x <= high; ++x) result.push_back(1LL * k * base + x * step);
} else {
if (high >= (1 << 23)) throw std::length_error("sum range is too large");
auto bound = fixed_size_subset_sum_detail::coefficient_bound(count, k);
std::vector<unsigned char> present(std::size_t(high - low) + 1);
std::size_t missing = present.size();
fixed_size_subset_sum_detail::Digits product{1};
for (auto pass : fixed_size_subset_sum_detail::passes) {
missing -= pass.include(values, k, int(low), present);
fixed_size_subset_sum_detail::multiply(product, pass.modulus);
if (!missing || fixed_size_subset_sum_detail::less(bound, product)) break;
int left = 0, right = 0, candidates = 0;
long long small = low - values[k - 1], large = high - values[count - k];
for (std::size_t i = 0; i < present.size() && candidates < count; ++i) {
if (present[i]) continue;
long long sum = low + i;
while (left < count && values[left] < sum - large) ++left;
while (right < count && values[right] <= sum - small) ++right;
candidates = std::max(candidates, right - left);
}
bound = fixed_size_subset_sum_detail::coefficient_bound(candidates, k);
if (fixed_size_subset_sum_detail::less(bound, product)) break;
}
result.reserve(present.size() - missing);
for (std::size_t i = 0; i < present.size(); ++i)
if (present[i]) result.push_back(1LL * k * base + (low + i) * step);
}
}
if (complement) {
for (auto& x : result) x = total - x;
std::reverse(result.begin(), result.end());
}
return result;
}