Atcoder Library Lazy Segment Tree Implementation Bug (Probably) + Polynomial Queries CSES Bonus
Difference between en3 and en4, changed 1,446 character(s)
With **Atcoder API** of **Lazy Segment Tree,** [Lazy Seg Tree Template](https://github.com/atcoder/ac-library/blob/master/atcoder/lazysegtree.hpp) <br> I am trying the problem where we need to support query to increment a range with a given value.↵

The Atcoder API implementation seems to fail here, where I have an array.↵
<br>↵

  **[3, 2, 4, 5, 1, 1, 5, 3]**↵
<br>↵


and I want to do a range update over the range **[1,4] ( 0 based indexing)** and **add a value 1** to it.↵
<br>↵
The sum should ideally change to **16** after the update and the **array** should look like <br>↵

   **[3, 3, 5, 6, 2, 1, 5, 3]**↵
<br>↵


The query function , the **.prod()** function of Atcoder API seems to return result **15**,<br> You can try running this code for your reference to understand the ambiguity happening here.↵


<spoiler summary="Code">↵
~~~~~↵
#include<bits/stdc++.h>↵
namespace internal {↵
    unsigned int bit_ceil(unsigned int n) {↵
    unsigned int x = 1;↵
    while (x < (unsigned int)(n)) x *= 2;↵
    return x;↵
    }↵
    int countr_zero(unsigned int n) {↵
    #ifdef _MSC_VER↵
        unsigned long index;↵
        _BitScanForward(&index, n);↵
        return index;↵
    #else↵
        return __builtin_ctz(n);↵
    #endif↵
    }↵
    constexpr int countr_zero_constexpr(unsigned int n) {↵
        int x = 0;↵
        while (!(n & (1 << x))) x++;↵
        return x;↵
    }↵
}↵
template <class S,↵
          S (*op)(S, S),↵
          S (*e)(),↵
          class F,↵
          S (*mapping)(F, S),↵
          F (*composition)(F, F),↵
          F (*id)()>↵
struct lazy_segtree {↵

  public:↵
    lazy_segtree() : lazy_segtree(0) {}↵
    explicit lazy_segtree(int n) : lazy_segtree(std::vector<S>(n, e())) {}↵
    explicit lazy_segtree(const std::vector<S>& v) : _n(int(v.size())) {↵
        size = (int)internal::bit_ceil((unsigned int)(_n));↵
        log = internal::countr_zero((unsigned int)size);↵
        d = std::vector<S>(2 * size, e());↵
        lz = std::vector<F>(size, id());↵
        for (int i = 0; i < _n; i++) d[size + i] = v[i];↵
        for (int i = size - 1; i >= 1; i--) {↵
            update(i);↵
        }↵
    }↵

    void set(int p, S x) {↵
        assert(0 <= p && p < _n);↵
        p += size;↵
        for (int i = log; i >= 1; i--) push(p >> i);↵
        d[p] = x;↵
        for (int i = 1; i <= log; i++) update(p >> i);↵
    }↵

    S get(int p) {↵
        assert(0 <= p && p < _n);↵
        p += size;↵
        for (int i = log; i >= 1; i--) push(p >> i);↵
        return d[p];↵
    }↵

    S prod(int l, int r) {↵
        assert(0 <= l && l <= r && r <= _n);↵
        if (l == r) return e();↵

        l += size;↵
        r += size;↵

        for (int i = log; i >= 1; i--) {↵
            if (((l >> i) << i) != l) push(l >> i);↵
            if (((r >> i) << i) != r) push((r - 1) >> i);↵
        }↵

        S sml = e(), smr = e();↵
        while (l < r) {↵
            if (l & 1) sml = op(sml, d[l++]);↵
            if (r & 1) smr = op(d[--r], smr);↵
            l >>= 1;↵
            r >>= 1;↵
        }↵

        return op(sml, smr);↵
    }↵

    S all_prod() { return d[1]; }↵

    void apply(int p, F f) {↵
        assert(0 <= p && p < _n);↵
        p += size;↵
        for (int i = log; i >= 1; i--) push(p >> i);↵
        d[p] = mapping(f, d[p]);↵
        for (int i = 1; i <= log; i++) update(p >> i);↵
    }↵
    void apply(int l, int r, F f) {↵
        assert(0 <= l && l <= r && r <= _n);↵
        if (l == r) return;↵

        l += size;↵
        r += size;↵

        for (int i = log; i >= 1; i--) {↵
            if (((l >> i) << i) != l) push(l >> i);↵
            if (((r >> i) << i) != r) push((r - 1) >> i);↵
        }↵

        {↵
            int l2 = l, r2 = r;↵
            while (l < r) {↵
                if (l & 1) all_apply(l++, f);↵
                if (r & 1) all_apply(--r, f);↵
                l >>= 1;↵
                r >>= 1;↵
            }↵
            l = l2;↵
            r = r2;↵
        }↵

        for (int i = 1; i <= log; i++) {↵
            if (((l >> i) << i) != l) update(l >> i);↵
            if (((r >> i) << i) != r) update((r - 1) >> i);↵
        }↵
    }↵
  private:↵
    int _n, size, log;↵
    std::vector<S> d;↵
    std::vector<F> lz;↵

    void update(int k) { d[k] = op(d[2 * k], d[2 * k + 1]); }↵
    void all_apply(int k, F f) {↵
        d[k] = mapping(f, d[k]);↵
        if (k < size) lz[k] = composition(f, lz[k]);↵
    }↵
    void push(int k) {↵
        all_apply(2 * k, lz[k]);↵
        all_apply(2 * k + 1, lz[k]);↵
        lz[k] = id();↵
    }↵
};↵
int64_t add(int64_t a,int64_t b){↵
  return a+b;↵
}↵
int64_t e(){↵
  return 0;↵
}↵
struct F{↵
  int64_t x;↵
};↵
int64_t mapping(F f,int64_t s){↵
  return f.x+s;↵
}↵
F composition(F a,F b){↵
  return F{a.x+b.x};↵
}↵
F id(){↵
  return F{(int64_t)0};↵
}↵
signed main(){ ↵
  uint32_t N = 8;↵
  std::vector<int64_t> arr = {3, 2 ,4 ,5 ,1 ,1 ,5 ,3};↵
  lazy_segtree<int64_t,add,e,F,mapping,composition,id> tree(arr);↵
  tree.apply(1,5,F{1});↵
  for(uint32_t i=0;i<N;i++){↵
    std::cout << tree.get(i) << " ";↵
  }↵
  std::cout <<"\n";↵
  std::cout << tree.prod(1,5) << "\n";↵
}↵
~~~~~↵
</spoiler>↵

<spoiler summary="Output">↵
3 3 5 6 2 1 5 3<br>↵
15↵
</spoiler>↵

<br>↵
I am using CPP20 and I am pretty sure (almost 99%) that it is a bug in their implementation,<br> just want someone to acknowledge it or maybe tell what am I missing here. <br> ↵
cc [user:yosupo,2023-05-02] <br>↵

UPD -> It is resolved but yeah things are kinda confusing with this template. ↵

UPD 2-> This template is quite useful if you ever gone through the Polynomial queries question in  CSES section about adding AP to a range . It is one among the toughest ones.↵

Here is the complete structure to AC it using AtCoder Template.↵

<spoiler summary="Polynomial Queries CSES">↵

~~~~~↵
struct S{↵
    int64_t value,size,index;↵
};↵
S op(S a,S b){↵
    return S{a.value + b.value,a.size + b.size,a.index}; ↵
}↵
S id(){↵
  return S{0,0,-1};↵
}↵
struct F{↵
  int64_t ap,cd,index;                                                                                                                                                                       ↵
};↵
S mapping(F f, S s){↵
  int64_t value = s.value;↵
  int64_t size =  s.size;↵
  int64_t j = s.index;↵
  int64_t i = f.index;↵
  int64_t ap = f.ap;↵
  int64_t cd = f.cd;↵
  int64_t first_term = ap + (j-i)*cd;↵
  int64_t sum = ((size)*(2*first_term + (size-1)*cd))/2;↵
  return  S{value + sum ,size,j};↵
}↵
F composition(F f,F g){↵
  if(f.index > g.index){↵
    std::swap(f,g);↵
  }↵
  int64_t Aleft = f.ap;↵
  int64_t Aright = g.ap;↵
  int64_t posLeft = f.index;↵
  int64_t posRight = g.index;↵
  int64_t cdLeft = f.cd;↵
  int64_t cdRight = g.cd;↵
  int64_t finalA = Aleft + (posRight-posLeft)*cdLeft + Aright;↵
  int64_t finalcd = cdLeft + cdRight;↵
  return F{finalA,finalcd,posRight}; ↵
}↵
F e(){↵
  return F{0,0,0};↵
}↵
~~~~~↵


</spoiler>↵



History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en4 English KnightKnight 2023-05-06 01:02:23 1446
en3 English KnightKnight 2023-05-02 08:21:55 87
en2 English KnightKnight 2023-05-02 00:54:41 37 Tiny change: 'e. <br> \n[user:https://codeforces.net/profile/yosupo]\n' -> 'e. <br> \n\n' (published)
en1 English KnightKnight 2023-05-02 00:50:49 5474 Initial revision (saved to drafts)