From bf47bbfe9a7b9c08ecb80099a74ab99ec7cfed17 Mon Sep 17 00:00:00 2001 From: hitonanode <32937551+hitonanode@users.noreply.github.com> Date: Sun, 28 Apr 2024 17:48:03 +0900 Subject: [PATCH 1/4] beats: add random test --- segmenttree/test/beats_random_test.test.cpp | 92 +++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 segmenttree/test/beats_random_test.test.cpp diff --git a/segmenttree/test/beats_random_test.test.cpp b/segmenttree/test/beats_random_test.test.cpp new file mode 100644 index 00000000..d8fd33c9 --- /dev/null +++ b/segmenttree/test/beats_random_test.test.cpp @@ -0,0 +1,92 @@ +#define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_1_A" // DUMMY + +#include "../../random/xorshift.hpp" +#include "../acl_beats.hpp" + +#include +#include +#include +#include + +using RangeChMinMaxAddSum::S, RangeChMinMaxAddSum::F; + +int main() { + for (int trial = 0; trial < 1 << 20; ++trial) { + const int N = rand_int() % 32 + 1; + + const int maxA = rand_int() % 50 + 1; + const int Q = rand_int() % 10 + 1; + + std::vector A(N); + std::vector simulate(N); + for (int i = 0; i < N; ++i) { + simulate.at(i) = rand_int() % (maxA + 1); + A.at(i) = S(simulate.at(i), 1); + } + + segtree_beats + segtree(A); + + for (int q = 0; q < Q; ++q) { + int tp = rand_int() % 4; + if (q == Q - 1) tp = 3; + + int l = 0, r = 0; + while (l == r) { + l = rand_int() % (N + 1); + r = rand_int() % (N + 1); + if (l > r) std::swap(l, r); + } + + if (tp < 3) { + int b = rand_int() % (maxA + 1); + if (tp == 0) { + for (int i = l; i < r; ++i) simulate.at(i) = std::min(simulate.at(i), b); + segtree.apply(l, r, RangeChMinMaxAddSum::F::chmin(b)); + } + + if (tp == 1) { + for (int i = l; i < r; ++i) simulate.at(i) = std::max(simulate.at(i), b); + segtree.apply(l, r, RangeChMinMaxAddSum::F::chmax(b)); + } + + if (tp == 2) { + for (int i = l; i < r; ++i) simulate.at(i) += b; + segtree.apply(l, r, RangeChMinMaxAddSum::F::add(b)); + } + } + + if (tp == 3) { + auto prod = segtree.prod(l, r); + + std::vector values; + for (int i = l; i < r; ++i) values.push_back(simulate.at(i)); + std::sort(values.begin(), values.end()); + + assert(prod.lo == values.front()); + assert(prod.nlo == std::count(values.begin(), values.end(), prod.lo)); + + assert(prod.hi == values.back()); + assert(prod.nhi == std::count(values.begin(), values.end(), prod.hi)); + + assert(prod.sum == std::accumulate(values.begin(), values.end(), 0LL)); + + assert(prod.sz == r - l); + assert(!prod.fail); + + if (values.front() != values.back()) { + int i = 0; + while (values.at(i) == values.front()) ++i; + assert(prod.lo2 == values.at(i)); + + i = (int)values.size() - 1; + while (values.at(i) == values.back()) --i; + assert(prod.hi2 == values.at(i)); + } + } + } + } + + puts("Hello World"); +} From c86325392e747416cea75af47831bceb4d077ebb Mon Sep 17 00:00:00 2001 From: hitonanode <32937551+hitonanode@users.noreply.github.com> Date: Sun, 28 Apr 2024 17:05:21 +0900 Subject: [PATCH 2/4] fix beats bug --- segmenttree/acl_beats.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/segmenttree/acl_beats.hpp b/segmenttree/acl_beats.hpp index 40504808..77c5e926 100644 --- a/segmenttree/acl_beats.hpp +++ b/segmenttree/acl_beats.hpp @@ -76,7 +76,7 @@ F composition(F fnew, F fold) { F id() { return F(); } S mapping(F f, S x) { if (x.sz == 0) return e(); - if (x.lo == x.hi or f.lb == f.ub or f.lb >= x.hi or f.ub < x.lo) + if (x.lo == x.hi or f.lb == f.ub or f.lb >= x.hi or f.ub <= x.lo) return S(std::min(std::max(x.lo, f.lb), f.ub) + f.bias, x.sz); if (x.lo2 == x.hi) { x.lo = x.hi2 = std::max(x.lo, f.lb) + f.bias, x.hi = x.lo2 = std::min(x.hi, f.ub) + f.bias; From 5dc514109dcc62c00c9b96b044b0e57d76ac7e9b Mon Sep 17 00:00:00 2001 From: hitonanode <32937551+hitonanode@users.noreply.github.com> Date: Sun, 28 Apr 2024 17:28:53 +0900 Subject: [PATCH 3/4] acl_beats: add comments --- segmenttree/acl_beats.hpp | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/segmenttree/acl_beats.hpp b/segmenttree/acl_beats.hpp index 77c5e926..4396b131 100644 --- a/segmenttree/acl_beats.hpp +++ b/segmenttree/acl_beats.hpp @@ -1,7 +1,6 @@ #pragma once #include "acl_lazysegtree.hpp" -// CUT begin template class segtree_beats : public atcoder::lazy_segtree { @@ -19,12 +18,14 @@ class segtree_beats : public atcoder::lazy_segtree -template -inline Num second_lowest(Num a, Num a2, Num c, Num c2) noexcept { // a < a2, c < c2 +template inline Num second_lowest(Num a, Num a2, Num c, Num c2) noexcept { + assert(a <= a2); // a < a2 or a == a2 == INF + assert(c <= c2); // c < c2 or c == c2 == -INF return a == c ? std::min(a2, c2) : a2 <= c ? a2 : c2 <= a ? c2 : std::max(a, c); } -template -inline Num second_highest(Num a, Num a2, Num b, Num b2) noexcept { // a > a2, b > b2 +template inline Num second_highest(Num a, Num a2, Num b, Num b2) noexcept { + assert(a >= a2); // a > a2 or a == a2 == -INF + assert(b >= b2); // b > b2 or b == b2 == INF return a == b ? std::max(a2, b2) : a2 >= b ? a2 : b2 >= a ? b2 : std::min(a, b); } @@ -36,7 +37,7 @@ struct S { unsigned sz, nlo, nhi; bool fail; S() : lo(BINF), hi(-BINF), lo2(BINF), hi2(-BINF), sum(0), sz(0), nlo(0), nhi(0), fail(0) {} - S(BNum x, unsigned sz_ = 1) + S(BNum x, unsigned sz_) : lo(x), hi(x), lo2(BINF), hi2(-BINF), sum(x * sz_), sz(sz_), nlo(sz_), nhi(sz_), fail(0) {} friend std::ostream &operator<<(std::ostream &os, const S s) { return os << "[(" << s.lo << "x" << s.nlo << ", " << s.lo2 << ", " << s.hi2 << ", " << s.hi @@ -49,10 +50,12 @@ S op(S l, S r) { if (l.lo > l.hi) return r; if (r.lo > r.hi) return l; S ret; - ret.lo = std::min(l.lo, r.lo), ret.hi = std::max(l.hi, r.hi); + ret.lo = std::min(l.lo, r.lo); + ret.hi = std::max(l.hi, r.hi); ret.lo2 = second_lowest(l.lo, l.lo2, r.lo, r.lo2), ret.hi2 = second_highest(l.hi, l.hi2, r.hi, r.hi2); - ret.sum = l.sum + r.sum, ret.sz = l.sz + r.sz; + ret.sum = l.sum + r.sum; + ret.sz = l.sz + r.sz; ret.nlo = l.nlo * (l.lo <= r.lo) + r.nlo * (r.lo <= l.lo); ret.nhi = l.nhi * (l.hi >= r.hi) + r.nhi * (r.hi >= l.hi); return ret; @@ -76,21 +79,32 @@ F composition(F fnew, F fold) { F id() { return F(); } S mapping(F f, S x) { if (x.sz == 0) return e(); - if (x.lo == x.hi or f.lb == f.ub or f.lb >= x.hi or f.ub <= x.lo) + + // f の作用後 x の要素が 1 種類だけになるケース + if (x.lo == x.hi or f.lb == f.ub or f.lb >= x.hi or f.ub <= x.lo) { return S(std::min(std::max(x.lo, f.lb), f.ub) + f.bias, x.sz); + } + + // 2 種類 -> 1 種類 if (x.lo2 == x.hi) { - x.lo = x.hi2 = std::max(x.lo, f.lb) + f.bias, x.hi = x.lo2 = std::min(x.hi, f.ub) + f.bias; + x.lo = x.hi2 = std::max(x.lo, f.lb) + f.bias; + x.hi = x.lo2 = std::min(x.hi, f.ub) + f.bias; x.sum = x.lo * x.nlo + x.hi * x.nhi; return x; } + + // lo と lo2, hi と hi2 が潰れないケース if (f.lb < x.lo2 and f.ub > x.hi2) { BNum nxt_lo = std::max(x.lo, f.lb), nxt_hi = std::min(x.hi, f.ub); x.sum += (nxt_lo - x.lo) * x.nlo - (x.hi - nxt_hi) * x.nhi + f.bias * x.sz; x.lo = nxt_lo + f.bias, x.hi = nxt_hi + f.bias, x.lo2 += f.bias, x.hi2 += f.bias; return x; } + x.fail = 1; return x; } + using segtree = segtree_beats; + } // namespace RangeChMinMaxAddSum From b42e508720b0305f1efe36fd9ec0ffe3023ee03c Mon Sep 17 00:00:00 2001 From: hitonanode <32937551+hitonanode@users.noreply.github.com> Date: Sun, 28 Apr 2024 17:37:36 +0900 Subject: [PATCH 4/4] Fix gcd beats to pass CI --- .../trees/acl_range-update-gcd-range-max-sum.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/segmenttree/trees/acl_range-update-gcd-range-max-sum.hpp b/segmenttree/trees/acl_range-update-gcd-range-max-sum.hpp index 7505b2f9..08fbe223 100644 --- a/segmenttree/trees/acl_range-update-gcd-range-max-sum.hpp +++ b/segmenttree/trees/acl_range-update-gcd-range-max-sum.hpp @@ -1,7 +1,9 @@ #pragma once #include "../acl_beats.hpp" -// CUT begin +#include +#include + // Verified: https://yukicoder.me/submissions/611774 namespace yuki880 { constexpr uint32_t BINF = 1 << 30; @@ -28,7 +30,7 @@ S op(S l, S r) { ret.sum = l.sum + r.sum; ret.lcm = (l.lcm >= BINF or r.lcm >= BINF) ? BINF - : std::min(BINF, (uint64_t)l.lcm * r.lcm / std::__gcd(l.lcm, r.lcm)); + : std::min(BINF, (uint64_t)l.lcm * r.lcm / std::gcd(l.lcm, r.lcm)); ret.sz = l.sz + r.sz; if (l.all_same and r.all_same and l.max == r.max) ret.all_same = true; return ret; @@ -43,7 +45,7 @@ struct F { }; F composition(F fnew, F fold) { - return fnew.reset ? fnew : F(std::__gcd(fnew.dogcd, fold.dogcd), fold.reset); + return fnew.reset ? fnew : F(std::gcd(fnew.dogcd, fold.dogcd), fold.reset); } F id() { return F(); } @@ -53,7 +55,7 @@ S mapping(F f, S x) { if (f.reset) x = S(f.reset, x.sz); if (f.dogcd) { if (x.all_same) - x = S(std::__gcd(f.dogcd, x.max), x.sz); + x = S(std::gcd(f.dogcd, x.max), x.sz); else if (f.dogcd and (x.lcm == BINF or f.dogcd % x.lcm)) x.fail = true; }