競技プログラミング用 C++ ライブラリ
components/insert_only_prefix_count_sum.hpp · GitHub で見る
共通ページ
値の上限付き区間和で使う、値の挿入と区間の和・個数の集計部品です。局所的な集計値が 64 bit に収まることを確認してから利用します。
insert_only_prefix_detail::Packed(n,minimum,bits) の insert(p,delta) は、位置 p に値 minimum+delta を挿入します。各位置への挿入は一度とし、各 65536 要素の区画内で個数と delta の和を連結した値が 64 bit に収まる bits を渡します。prefix(r) は [0,r)、fold(l,r) は [l,r) の集計を CountSum{sum,count} で返します。sum は __int128_t です。
構築・空間は \(O(n)\)、挿入・集計は \(O(\log n)\) です。
#ifndef INSERT_ONLY_PREFIX_COUNT_SUM_HPP
#define INSERT_ONLY_PREFIX_COUNT_SUM_HPP
#include "fenwick_sum.hpp"
#include <algorithm>
#include <array>
#include <bit>
#include <cassert>
#include <cstdint>
#include <vector>
#if defined(__x86_64__) || defined(__i386__)
#include <immintrin.h>
#endif
namespace insert_only_prefix_detail {
inline constexpr int level_count = 4, block_bits = 4 * level_count, block_size = 1 << block_bits;
struct CountSum {
__int128_t sum = 0;
int count = 0;
CountSum &operator+=(const CountSum &other) { sum += other.sum; count += other.count; return *this; }
CountSum &operator-=(const CountSum &other) { sum -= other.sum; count -= other.count; return *this; }
};
class Packed {
std::array<std::vector<std::array<std::uint64_t, 16>>, level_count> levels;
FenwickSum<CountSum> coarse;
__int128_t base;
std::uint64_t sum_mask;
int sum_bits;
[[maybe_unused]] bool vectorized = false;
static void add_scalar(std::uint64_t *values, int first, std::uint64_t delta) {
for (int i = first; i < 16; ++i) values[i] += delta;
}
#if defined(__x86_64__) || defined(__i386__)
__attribute__((target("avx2"), always_inline)) static void add_avx2(std::uint64_t *values, int first, std::uint64_t delta) {
if (first == 16) return;
int i = first & ~3;
auto change = _mm256_set1_epi64x(static_cast<long long>(delta));
auto active = _mm256_cmpgt_epi64(_mm256_setr_epi64x(i, i + 1, i + 2, i + 3), _mm256_set1_epi64x(first - 1));
auto p = reinterpret_cast<__m256i *>(values + i);
_mm256_storeu_si256(p, _mm256_add_epi64(_mm256_loadu_si256(p), _mm256_and_si256(change, active)));
for (i += 4; i < 16; i += 4) {
p = reinterpret_cast<__m256i *>(values + i);
_mm256_storeu_si256(p, _mm256_add_epi64(_mm256_loadu_si256(p), change));
}
}
#endif
#if defined(__x86_64__) || defined(__i386__)
__attribute__((target("avx2"))) void insert_avx2(unsigned p, std::uint64_t delta) {
auto encoded = (std::uint64_t(1) << sum_bits) + delta;
add_avx2(levels[0][p >> 4].data(), p & 15, encoded);
for (int level = 1; level < level_count; ++level)
add_avx2(levels[level][p >> (4 * (level + 1))].data(), ((p >> (4 * level)) & 15) + 1, encoded);
coarse.add(p >> block_bits, {base + delta, 1});
}
#endif
public:
Packed(int n, __int128_t minimum, int bits) : coarse((std::size_t(n) + block_size - 1) >> block_bits), base(minimum), sum_mask((std::uint64_t(1) << bits) - 1), sum_bits(bits) {
assert(n >= 0 && 0 < bits && bits < 64);
for (int level = 0; level < level_count; ++level) levels[level].resize((std::size_t(n) + (std::size_t(1) << (4 * (level + 1))) - 1) >> (4 * (level + 1)));
#if defined(__x86_64__) || defined(__i386__)
vectorized = __builtin_cpu_supports("avx2");
#endif
}
void insert(int p, std::uint64_t delta) {
#if defined(__x86_64__) || defined(__i386__)
if (vectorized) { insert_avx2(unsigned(p), delta); return; }
#endif
auto encoded = (std::uint64_t(1) << sum_bits) + delta;
add_scalar(levels[0][unsigned(p) >> 4].data(), p & 15, encoded);
for (int level = 1; level < level_count; ++level)
add_scalar(levels[level][unsigned(p) >> (4 * (level + 1))].data(), ((unsigned(p) >> (4 * level)) & 15) + 1, encoded);
coarse.add(unsigned(p) >> block_bits, {base + delta, 1});
}
CountSum prefix(int r) const {
if (!r) return {};
unsigned p = unsigned(r - 1);
std::uint64_t encoded = levels[0][p >> 4][p & 15];
for (int level = 1; level < level_count; ++level) encoded += levels[level][p >> (4 * (level + 1))][(p >> (4 * level)) & 15];
auto result = coarse.prefix(p >> block_bits);
int count = int(encoded >> sum_bits);
result.sum += __int128_t(encoded & sum_mask) + base * count;
result.count += count;
return result;
}
CountSum fold(int l, int r) const {
auto result = prefix(r);
return result -= prefix(l);
}
};
}
#endif
#line 1 "components/insert_only_prefix_count_sum.hpp"
#line 1 "components/fenwick_sum.hpp"
#include <cassert>
#include <bit>
#include <limits>
#include <vector>
template <class T>
class FenwickSum {
std::vector<T> data;
public:
FenwickSum() = default;
explicit FenwickSum(int n) { assert(n >= 0); data.resize(n); }
explicit FenwickSum(const std::vector<T> &a) { build(a); }
void build(const std::vector<T> &a) {
assert(a.size() <= static_cast<unsigned>(std::numeric_limits<int>::max()));
data = a;
constexpr unsigned step = sizeof(T) <= 8 ? 2 : 1;
for (unsigned i = step; i <= data.size(); i += step) {
if constexpr (step == 2) data[i - 1] += data[i - 2];
unsigned p = i + (i & -i);
if (p <= data.size()) data[p - 1] += data[i - 1];
}
}
int size() const { return static_cast<int>(data.size()); }
void add(int p, const T &x) {
assert(0 <= p && p < size());
for (unsigned i = unsigned(p) + 1; i <= data.size(); i += i & -i) data[i - 1] += x;
}
void add_pair(int p, const T &x, int q, const T &y) {
assert(0 <= p && p < size() && 0 <= q && q < size());
unsigned a = unsigned(p) + 1, b = unsigned(q) + 1;
while (a != b && (a <= data.size() || b <= data.size())) {
if (a < b) data[a - 1] += x, a += a & -a;
else data[b - 1] += y, b += b & -b;
}
T both = x;
both += y;
for (; a <= data.size(); a += a & -a) data[a - 1] += both;
}
T prefix(int r) const {
assert(0 <= r && r <= size());
T result{};
for (; r; r &= r - 1) result += data[r - 1];
return result;
}
template <class Accum>
Accum prod_as(int l, int r) const {
assert(0 <= l && l <= r && r <= size());
Accum result{};
if (l == r) return result;
unsigned shared = unsigned(l) & (std::numeric_limits<unsigned>::max() << std::bit_width(unsigned(l ^ r)));
for (; unsigned(l) != shared; l &= l - 1) result -= data[l - 1];
for (; unsigned(r) != shared; r &= r - 1) result += data[r - 1];
return result;
}
T prod(int l, int r) const { return prod_as<T>(l, r); }
};
#line 5 "components/insert_only_prefix_count_sum.hpp"
#include <algorithm>
#include <array>
#line 9 "components/insert_only_prefix_count_sum.hpp"
#include <cstdint>
#line 11 "components/insert_only_prefix_count_sum.hpp"
#if defined(__x86_64__) || defined(__i386__)
#include <immintrin.h>
#endif
namespace insert_only_prefix_detail {
inline constexpr int level_count = 4, block_bits = 4 * level_count, block_size = 1 << block_bits;
struct CountSum {
__int128_t sum = 0;
int count = 0;
CountSum &operator+=(const CountSum &other) { sum += other.sum; count += other.count; return *this; }
CountSum &operator-=(const CountSum &other) { sum -= other.sum; count -= other.count; return *this; }
};
class Packed {
std::array<std::vector<std::array<std::uint64_t, 16>>, level_count> levels;
FenwickSum<CountSum> coarse;
__int128_t base;
std::uint64_t sum_mask;
int sum_bits;
[[maybe_unused]] bool vectorized = false;
static void add_scalar(std::uint64_t *values, int first, std::uint64_t delta) {
for (int i = first; i < 16; ++i) values[i] += delta;
}
#if defined(__x86_64__) || defined(__i386__)
__attribute__((target("avx2"), always_inline)) static void add_avx2(std::uint64_t *values, int first, std::uint64_t delta) {
if (first == 16) return;
int i = first & ~3;
auto change = _mm256_set1_epi64x(static_cast<long long>(delta));
auto active = _mm256_cmpgt_epi64(_mm256_setr_epi64x(i, i + 1, i + 2, i + 3), _mm256_set1_epi64x(first - 1));
auto p = reinterpret_cast<__m256i *>(values + i);
_mm256_storeu_si256(p, _mm256_add_epi64(_mm256_loadu_si256(p), _mm256_and_si256(change, active)));
for (i += 4; i < 16; i += 4) {
p = reinterpret_cast<__m256i *>(values + i);
_mm256_storeu_si256(p, _mm256_add_epi64(_mm256_loadu_si256(p), change));
}
}
#endif
#if defined(__x86_64__) || defined(__i386__)
__attribute__((target("avx2"))) void insert_avx2(unsigned p, std::uint64_t delta) {
auto encoded = (std::uint64_t(1) << sum_bits) + delta;
add_avx2(levels[0][p >> 4].data(), p & 15, encoded);
for (int level = 1; level < level_count; ++level)
add_avx2(levels[level][p >> (4 * (level + 1))].data(), ((p >> (4 * level)) & 15) + 1, encoded);
coarse.add(p >> block_bits, {base + delta, 1});
}
#endif
public:
Packed(int n, __int128_t minimum, int bits) : coarse((std::size_t(n) + block_size - 1) >> block_bits), base(minimum), sum_mask((std::uint64_t(1) << bits) - 1), sum_bits(bits) {
assert(n >= 0 && 0 < bits && bits < 64);
for (int level = 0; level < level_count; ++level) levels[level].resize((std::size_t(n) + (std::size_t(1) << (4 * (level + 1))) - 1) >> (4 * (level + 1)));
#if defined(__x86_64__) || defined(__i386__)
vectorized = __builtin_cpu_supports("avx2");
#endif
}
void insert(int p, std::uint64_t delta) {
#if defined(__x86_64__) || defined(__i386__)
if (vectorized) { insert_avx2(unsigned(p), delta); return; }
#endif
auto encoded = (std::uint64_t(1) << sum_bits) + delta;
add_scalar(levels[0][unsigned(p) >> 4].data(), p & 15, encoded);
for (int level = 1; level < level_count; ++level)
add_scalar(levels[level][unsigned(p) >> (4 * (level + 1))].data(), ((unsigned(p) >> (4 * level)) & 15) + 1, encoded);
coarse.add(unsigned(p) >> block_bits, {base + delta, 1});
}
CountSum prefix(int r) const {
if (!r) return {};
unsigned p = unsigned(r - 1);
std::uint64_t encoded = levels[0][p >> 4][p & 15];
for (int level = 1; level < level_count; ++level) encoded += levels[level][p >> (4 * (level + 1))][(p >> (4 * level)) & 15];
auto result = coarse.prefix(p >> block_bits);
int count = int(encoded >> sum_bits);
result.sum += __int128_t(encoded & sum_mask) + base * count;
result.count += count;
return result;
}
CountSum fold(int l, int r) const {
auto result = prefix(r);
return result -= prefix(l);
}
};
}