cp-documentation

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub zawa-tin/cp-documentation

:heavy_check_mark: Test/LC/vertex_get_range_contour_add_on_tree.test.cpp

Depends on

Code

#define PROBLEM "https://judge.yosupo.jp/problem/vertex_get_range_contour_add_on_tree"

#include "../../Src/Graph/Tree/ContourAggregation.hpp"
#include "../../Src/DataStructure/FenwickTree/DualFenwickTree.hpp"
#include "../../Src/Algebra/Group/AdditiveGroup.hpp"
using namespace zawa;

#include <cassert>
#include <iostream>
#include <vector>
using namespace std;

int main() {
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    int N, Q;
    cin >> N >> Q;
    vector<int> A(N);
    for (auto& x : A)
        cin >> x;
    vector<vector<int>> G(N);
    for (int i = 0 ; i < N - 1 ; i++) {
       int u, v;
       cin >> u >> v;
       G[u].push_back(v);
       G[v].push_back(u);
    }
    ContourAggregation sol(move(G));
    cerr << "size=" << ssize(sol) << endl;
    DualFenwickTree<AdditiveGroup<long long>> fen(ssize(sol));
    for (int i = 0 ; i < N ; i++) 
        for (auto [L, R] : sol.contour(i,0))
            fen.operation(L,R,A[i]);
    while (Q--) {
        int T;
        cin >> T;
        if (T == 0) {
            int p, l, r, x;
            cin >> p >> l >> r >> x;
            for (auto [L, R] : sol.contour(p,l,r))
                fen.operation(L,R,x);
        }
        else if (T == 1) {
            int p;
            cin >> p;
            long long ans = 0;
            for (auto i : sol.point(p))
                ans += fen[i];
            cout << ans << '\n';
        }
        else
            assert(0);
    }
}
#line 1 "Test/LC/vertex_get_range_contour_add_on_tree.test.cpp"
#define PROBLEM "https://judge.yosupo.jp/problem/vertex_get_range_contour_add_on_tree"

#line 2 "Src/Graph/Tree/ContourAggregation.hpp"

#line 2 "Src/Graph/Tree/Centroid.hpp"

#line 2 "Src/Template/TypeAlias.hpp"

#include <cstdint>
#include <cstddef>

namespace zawa {

using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;

using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

using usize = std::size_t;

} // namespace zawa
#line 4 "Src/Graph/Tree/Centroid.hpp"

#include <cassert>
#include <utility>
#include <vector>

namespace zawa {

template <class V>
class Centroid {
public:

    Centroid() = default;

    Centroid(const std::vector<std::vector<V>>& T) : T_{T}, size_(T_.size(), usize{1}) {}
    Centroid(std::vector<std::vector<V>>&& T) : T_{std::move(T)}, size_(T_.size(), usize{1}) {}

    inline usize size() const noexcept {
        return T_.size();
    }

    inline usize size(V v) const noexcept {
        assert(v < (V)size());
        return size_[v];
    }

    bool isRemoved(V v) const noexcept {
        assert(v < (V)size());
        return size_[v] == 0u;
    }

    void remove(V v) noexcept {
        assert(v < (V)size());
        size_[v] = 0u;
    }

    const std::vector<V>& operator[](V v) const noexcept {
        assert(v < (V)size());
        return T_[v];
    }

    // @response: centroid of component which v belongs
    V rooting(V v) {
        assert(v < (V)size());
        assert(!isRemoved(v));
        usize all{dfsSize(v, INVALID)};
        V par{INVALID};
        bool fn{false};
        while (!fn) {
            fn = true;
            for (V x : T_[v]) {
                if (x == par or isRemoved(x) or usize{2} * size_[x] <= all) {
                    continue;
                }
                fn = false;
                par = v;
                v = x;
                break;
            }
        }
        return v;
    }

    std::vector<V> component(V v) const {
        assert(v < (V)size());
        assert(!isRemoved(v));
        std::vector<V> res;
        dfsComponent(v, INVALID, res);
        return res;
    }

    std::vector<V> adjlist(V v) const {
        assert(v < (V)size());
        std::vector<V> res;
        res.reserve(T_[v].size());
        for (auto x : T_[v]) if (!isRemoved(x)) {
            res.emplace_back(x);
        }
        return res;
    }

private:
    static constexpr V INVALID{static_cast<V>(-1)};
    std::vector<std::vector<V>> T_{};
    std::vector<usize> size_{};

    usize dfsSize(V v, V par) {
        size_[v] = 1u;
        for (V x : T_[v]) if (x != par and !isRemoved(x)) {
            size_[v] += dfsSize(x, v);
        }
        return size_[v];
    }

    void dfsComponent(V v, V par, std::vector<V>& comp) const {
        comp.emplace_back(v);
        for (V x : T_[v]) if (x != par and !isRemoved(x)) {
            dfsComponent(x, v, comp);
        }
    }
};

} // namespace zawa
#line 2 "Src/Graph/Tree/LowestCommonAncestor.hpp"

#line 2 "Src/Algebra/Monoid/ChminMonoid.hpp"

#line 4 "Src/Algebra/Monoid/ChminMonoid.hpp"

#include <algorithm>
#include <optional>

namespace zawa {

template <class T, class U>
class ChminMonoidData {
private:
    std::optional<T> priority_{};
    U value_{};
public:
    ChminMonoidData() = default;
    ChminMonoidData(const U& value)
        : priority_{std::nullopt}, value_{value} {}
    ChminMonoidData(const T& priority, const U& value)
        : priority_{priority}, value_{value} {}

    constexpr bool infty() const noexcept {
        return !priority_.has_value();
    }
    constexpr const T& priority() const noexcept {
        return priority_.value();
    }
    constexpr const U& value() const noexcept {
        return value_;
    }
    friend constexpr bool operator<(const ChminMonoidData& l, const ChminMonoidData& r) {
        if (l.infty()) return false;
        else if (r.infty()) return true;
        else return l.priority() < r.priority();
    }
};

template <class T, class U>
struct ChminMonoid {
    using Element = ChminMonoidData<T, U>;
    static Element identity() noexcept {
        return Element{};
    }
    static Element operation(const Element& l, const Element& r) noexcept {
        return (r < l ? r : l);
    }
};

} // namespace zawa
#line 2 "Src/DataStructure/SparseTable/SparseTable.hpp"

#line 4 "Src/DataStructure/SparseTable/SparseTable.hpp"

#line 7 "Src/DataStructure/SparseTable/SparseTable.hpp"
#include <ostream>

namespace zawa {

template <class Structure>
class SparseTable {
private:
    using Value = typename Structure::Element;
    std::vector<u32> L;
    std::vector<std::vector<Value>> dat;
public:

    SparseTable() : L{}, dat{} {}
    SparseTable(const std::vector<Value>& a) : L(a.size() + 1), dat{} {
        for (u32 i{1} ; i < L.size() ; i++) {
            L[i] = L[i - 1] + (i >> (L[i - 1] + 1));
        }
        dat.resize(L.back() + 1);
        dat[0] = a;
        for (u32 i{1}, len{2} ; i < dat.size() ; i++, len <<= 1) {
            dat[i] = dat[i - 1];
            for (u32 j{} ; j + len - 1 < dat[i].size() ; j++) {
                dat[i][j] = Structure::operation(dat[i - 1][j], dat[i - 1][j + (len >> 1)]);
            }
        }
    }

    Value product(u32 l, u32 r) const {
        assert(l <= r);
        assert(l < dat[0].size());
        assert(r <= dat[0].size());
        u32 now{L[r - l]};
        return Structure::operation(dat[now][l], dat[now][r - (1 << now)]);
    }

    friend std::ostream& operator<<(std::ostream& os, const SparseTable<Structure>& spt) {
        for (u32 i{}, len{1} ; i < spt.dat.size() ; i++, len <<= 1) {
            os << "length = " << len << '\n';
            for (u32 j{} ; j + len - 1 < spt.dat[i].size() ; j++) {
                os << spt.dat[i][j] << (j + len == spt.dat[i].size() ? '\n' : ' ');
            }
        }
        return os;
    }
};

} // namespace zawa
#line 6 "Src/Graph/Tree/LowestCommonAncestor.hpp"

#line 9 "Src/Graph/Tree/LowestCommonAncestor.hpp"

namespace zawa {

template <class V>
class LowestCommonAncestor {
private:
    using Monoid = ChminMonoid<u32, V>;

public:
    LowestCommonAncestor() = default;

    LowestCommonAncestor(const std::vector<std::vector<V>>& tree, V r = V{}) 
        : n_{tree.size()}, depth_(tree.size()), L_(tree.size()), R_(tree.size()), st_{} {
            std::vector<typename Monoid::Element> init;
            init.reserve(2 * size());
            auto dfs{[&](auto dfs, V v, V p) -> void {
                depth_[v] = (p == INVALID ? 0u : depth_[p] + 1);
                L_[v] = (u32)init.size();
                for (auto x : tree[v]) {
                    if (x == p) {
                        continue;
                    }
                    init.emplace_back(depth_[v], v);
                    dfs(dfs, x, v);
                }
                R_[v] = (u32)init.size();
            }};
            dfs(dfs, r, INVALID);
            st_ = SparseTable<Monoid>(init);
    }

    V operator()(V u, V v) const {
        assert(verify(u));
        assert(verify(v));
        if (L_[u] > L_[v]) {
            std::swap(u, v);
        }
        return u == v ? u : st_.product(L_[u], R_[v]).value();
    }

    V lca(V u, V v) const {
        return (*this)(u, v);
    }

    inline u32 depth(V v) const noexcept {
        assert(verify(v));
        return depth_[v];
    }

    u32 distance(V u, V v) const {
        assert(verify(u));
        assert(verify(v));
        return depth(u) + depth(v) - 2u * depth((*this)(u, v));
    }

    bool isAncestor(V p, V v) const {
        assert(verify(p));
        assert(verify(v));
        return L_[p] <= L_[v] and R_[v] <= R_[p];
    }

protected:
    u32 left(V v) const noexcept {
        return L_[v];
    }

    inline usize size() const {
        return n_;
    }

    inline bool verify(V v) const {
        return v < (V)size();
    }

private:
    static constexpr V INVALID{static_cast<V>(-1)};
    usize n_{};
    std::vector<u32> depth_, L_, R_;
    SparseTable<Monoid> st_;
};

} // namespace zawa
#line 2 "Src/DataStructure/Heap/BinaryHeap.hpp"

#line 4 "Src/DataStructure/Heap/BinaryHeap.hpp"

#line 7 "Src/DataStructure/Heap/BinaryHeap.hpp"
#include <concepts>
#line 10 "Src/DataStructure/Heap/BinaryHeap.hpp"
#include <functional>

namespace zawa {

template <class T, class Comp = std::less<T>>
requires std::strict_weak_order<Comp, const T&, const T&>
class BinaryHeap {
private:

    Comp m_comp;

    std::vector<T> m_dat;

public:

    inline usize size() const {
        return m_dat.size() - 1;
    }

    inline bool empty() const {
        return m_dat.size() == 1;
    }

    inline const Comp& comp() const {
        return m_comp;
    }

    using const_iterator = typename decltype(m_dat)::const_iterator;

    const_iterator begin() const {
        return m_dat.begin() + 1;
    }

    const_iterator end() const {
        return m_dat.end();
    }

    BinaryHeap(Comp comp = {}) 
        : m_comp{comp}, m_dat(1) {}

    template <std::forward_iterator It>
    requires std::same_as<std::iter_value_t<It>, T>
    BinaryHeap(It first, It last, Comp comp = {}) 
        : m_comp{comp}, m_dat(1) {
        m_dat.insert(m_dat.end(), first, last);
        build();
    }

    BinaryHeap(std::vector<T>&& a, Comp comp = {}) 
        : m_comp{comp}, m_dat(a.size() + 1) {
        std::ranges::copy(std::make_move_iterator(a.begin()), std::make_move_iterator(a.end()), m_dat.begin() + 1);
        build();
    }

    BinaryHeap(const std::vector<T>& a, Comp comp = {}) 
        : m_comp{comp}, m_dat(a.size() + 1) {
        std::ranges::copy(a.begin(), a.end(), m_dat.begin() + 1);
        build();
    }

    const T& top() const {
        assert(size() and "HeapUnderFlow");
        return m_dat[1];
    }

    void push(T&& v) {
        m_dat.push_back(std::move(v));
        upHeap(size());
    }

    void push(const T& v) {
        m_dat.push_back(v);
        upHeap(size());
    }

    void pop() {
        assert(size() and "HeapUnderFlow");
        if (size() > 1)
            std::swap(m_dat[1], m_dat.back());
        m_dat.pop_back();
        if (size() > 1)
            downHeap(1, size());
    }

private:

    void build() {
        const usize n = size();
        for (usize i = (n >> 1) ; i ; i--) 
            downHeap(i, n);
    }

    void upHeap(usize i) {
        while (i >> 1 and m_comp(m_dat[i], m_dat[i >> 1])) {
            std::swap(m_dat[i], m_dat[i >> 1]);
            i >>= 1;
        }
    }

    void downHeap(usize i, usize n) {
        while ((i << 1) <= n) {
            usize j = i << 1;
            if (j + 1 <= n and m_comp(m_dat[j + 1], m_dat[j]))
                j++;
            if (!m_comp(m_dat[j], m_dat[i]))
                break;
            std::swap(m_dat[i], m_dat[j]);
            i = j;
        }
    }
};

} // namespace zawa
#line 6 "Src/Graph/Tree/ContourAggregation.hpp"

#line 9 "Src/Graph/Tree/ContourAggregation.hpp"
#include <numeric>
#include <tuple>
#line 13 "Src/Graph/Tree/ContourAggregation.hpp"

namespace zawa {

template <std::integral V>
class ContourAggregation {
public:

    ContourAggregation(std::vector<std::vector<V>> G) : m_lca{G}, m_cent(1), m_par(1), m_ch(1), m_offset(1) {
        const usize N = G.size();
        assert(N);
        m_inv.resize(N);
        m_pos.resize(N);
        m_ord.reserve(2 * N);
        Centroid ce{std::move(G)};
        std::vector<i32> dist(N,-1);

        auto makeNode = [&]() -> usize {
            const usize res = m_cent.size();
            m_cent.push_back(N);
            m_par.push_back(0);
            m_ch.emplace_back();
            m_offset.emplace_back();
            return res;
        };

        auto createOffset = [&](usize nd,std::vector<V> vs) {
            std::vector<usize> offset{m_ord.size()};
            if (dist[vs[0]] == 1)
                offset.push_back(m_ord.size());
            for (usize i = 0, j = 0 ; i < vs.size() ; i = j) {
                while (j < vs.size() and dist[vs[i]] == dist[vs[j]]) {
                    m_inv[vs[j]].push_back(m_ord.size());
                    m_ord.push_back(vs[j]);
                    j++;
                }
                offset.push_back(m_ord.size());
            }
            m_offset[nd] = std::move(offset);
        };

        auto compDist = [&](usize i, usize j) -> bool {
            return dist[i] < dist[j];
        };

        // {node id, height, vertices}
        auto dfs = [&](auto dfs, V v) -> std::tuple<usize,usize,std::vector<V>> {
            v = ce.rooting(v);
            std::vector<std::tuple<usize,usize,std::vector<V>>> ch;
            ce.remove(v);
            dist[v] = 0;
            for (V x : ce.adjlist(v)) {
                auto ret = dfs(dfs,x);
                for (V cur : std::get<2>(ret))
                    dist[cur] = m_lca.distance(v,cur);
                m_cent[std::get<0>(ret)] = v;
                ch.push_back(std::move(ret));
            }
            for (auto& dat : ch)
                std::ranges::sort(std::get<2>(dat),compDist);
            { // create single node of {v}
                const usize nd = m_pos[v] = makeNode();
                ch.emplace_back(nd,0u,std::vector<V>{v});
                m_cent[nd] = v;
            }
            auto comp = [&](usize i, usize j) -> bool {
                return std::get<1>(ch[i]) < std::get<1>(ch[j]);
            };
            BinaryHeap<usize,decltype(comp)> heap{[&]()->std::vector<usize>{
                std::vector<usize> res(ch.size()); 
                std::iota(res.begin(),res.end(),0u); 
                return res;
            }(),comp};
            while (std::ssize(heap) > 1) {
                const usize l = heap.top();
                heap.pop();
                const usize r = heap.top();
                heap.pop();
                const usize L = std::get<0>(ch[l]), R = std::get<0>(ch[r]);
                createOffset(L,std::get<2>(ch[l]));
                createOffset(R,std::get<2>(ch[r]));
                // merge here
                std::vector<V> vertices;
                std::ranges::merge(std::move(std::get<2>(ch[l])),std::move(std::get<2>(ch[r])),std::back_inserter(vertices),compDist);
                const usize nd = makeNode();
                m_par[L] = m_par[R] = nd;
                m_cent[L] = m_cent[R] = v;
                const usize h = std::max(std::get<1>(ch[l]),std::get<1>(ch[r]))+1;
                ch.emplace_back(nd,h,std::move(vertices));
                heap.push(ch.size()-1);
                m_ch[nd] = {L,R};
            }
            for (V x : std::get<2>(ch[heap.top()]))
                dist[x] = -1;
            return ch[heap.top()];
        };
        if (N == 1) {
            m_inv[0].push_back(0);
            m_ord.push_back(0);
        }
        else {
            std::vector<bool> vis(N);
            for (V i = 0 ; i < (V)N ; i++)
                if (!vis[i]) {
                    const auto ret = std::get<2>(dfs(dfs,i));
                    for (V v : ret)
                        vis[v] = 1;
                }
        }
    }

    inline usize size() const {
        return m_ord.size();
    }

    std::vector<usize> point(V v) const {
        assert(0 <= v and v < (V)m_inv.size());
        return m_inv[v];
    }

    std::vector<std::pair<usize,usize>> contour(V v, usize l, usize r) const {
        assert(V{0} <= v and v < (V)m_inv.size());
        std::vector<std::pair<usize,usize>> res;
        if (l >= r)
            return res;
        usize nd = m_pos[v];
        if (l == 0)
            res.push_back({m_inv[v][0],m_inv[v][0]+1});
        for ( ; m_par[nd] ; nd = m_par[nd]) {
            const usize cur = m_ch[m_par[nd]].first == nd ? m_ch[m_par[nd]].second : m_ch[m_par[nd]].first;
            const usize sub = m_lca.distance(m_cent[cur],v);
            if (sub >= r)
                continue;
            const usize L = sub >= l ? 0 : l - sub;
            const usize R = std::min(r - sub,m_offset[cur].size()-1);
            if (L >= R)
                continue;
            res.push_back({m_offset[cur][L],m_offset[cur][R]});
        }
        return res;
    }

    std::vector<std::pair<usize,usize>> contour(V v, usize d) const {
        return contour(v,d,d+1);
    }

private:

    LowestCommonAncestor<V> m_lca;

    std::vector<V> m_cent;

    std::vector<usize> m_par;

    std::vector<std::pair<usize,usize>> m_ch;

    std::vector<std::vector<usize>> m_offset;

    std::vector<V> m_ord;

    std::vector<usize> m_pos;

    std::vector<std::vector<usize>> m_inv;
    
};

} // namespace zawa
#line 2 "Src/DataStructure/FenwickTree/DualFenwickTree.hpp"

#line 2 "Src/Algebra/Group/GroupConcept.hpp"

#line 2 "Src/Algebra/Monoid/MonoidConcept.hpp"

#line 2 "Src/Algebra/Semigroup/SemigroupConcept.hpp"

#line 4 "Src/Algebra/Semigroup/SemigroupConcept.hpp"

namespace zawa {

namespace concepts {

template <class T>
concept Semigroup = requires {
    typename T::Element;
    { T::operation(std::declval<typename T::Element>(), std::declval<typename T::Element>()) } -> std::same_as<typename T::Element>;
};

} // namespace concepts

} // namespace zawa
#line 4 "Src/Algebra/Monoid/MonoidConcept.hpp"

#line 6 "Src/Algebra/Monoid/MonoidConcept.hpp"

namespace zawa {

namespace concepts {

template <class T>
concept Identitiable = requires {
    typename T::Element;
    { T::identity() } -> std::same_as<typename T::Element>;
};

template <class T>
concept Monoid = Semigroup<T> and Identitiable<T>;

} // namespace

} // namespace zawa
#line 4 "Src/Algebra/Group/GroupConcept.hpp"

namespace zawa {

namespace concepts {

template <class T>
concept Inversible = requires {
    typename T::Element;
    { T::inverse(std::declval<typename T::Element>()) } -> std::same_as<typename T::Element>;
};

template <class T>
concept Group = Monoid<T> and Inversible<T>;

} // namespace Concept

} // namespace zawa
#line 5 "Src/DataStructure/FenwickTree/DualFenwickTree.hpp"

#include <bit>
#line 9 "Src/DataStructure/FenwickTree/DualFenwickTree.hpp"
#include <iterator>
#line 12 "Src/DataStructure/FenwickTree/DualFenwickTree.hpp"

namespace zawa {

namespace concepts {

template <class F, class V>
concept Predicate = requires {
    { std::declval<F>()(std::declval<V>()) } -> std::same_as<bool>; 
};

} // namespace Concept

template <concepts::Group G>
class DualFenwickTree {
public:

    using V = typename G::Element;

    constexpr static u32 Log2(usize n) noexcept {
        return static_cast<u32>(
                    std::bit_width(n) - (std::has_single_bit(n) ? 1 : 0)
                );
    }

    DualFenwickTree() = default;

    DualFenwickTree(usize n) : n_{n}, lg_{Log2(n)}, dat_(n+1, G::identity()) {
        assert(n);
    }

    DualFenwickTree(const std::vector<V>& d) : n_{d.size()}, lg_{Log2(n_)}, dat_(d.size() + 1, G::identity()) {
        assert(d.size());
        add(0, d[0]);
        for (usize i = 1 ; i < d.size() ; i++) add(i, G::operation(G::inverse(d[i - 1]), d[i]));
    }

    template <std::input_iterator It>
    DualFenwickTree(It first, It last) 
    : n_{static_cast<usize>(std::distance(first, last))}, lg_{Log2(n_)}, dat_(n_ + 1, G::identity()) {
        assert(first != last);
        V pv = static_cast<V>(*first);
        add(0, pv);
        for (usize i = 1 ; i < n_ ; i++) {
            first++;
            V v = static_cast<V>(*first);
            add(i, G::operation(G::inverse(pv), v));
            pv = v;
        } 
    }

    inline usize size() const noexcept {
        return n_;
    }

    void operation(usize l, usize r, const V& v) {
        assert(l <= r and r <= size());
        if (l < r) {
            add(l, v);
            if (r < size()) add(r, G::inverse(v));
        }
    }

    void operation(usize i, const V& v) {
        assert(i < size());
        operation(i, i + 1, v);
    }

    V operator[](i32 i) const {
        assert(0 <= i and i < (i32)size());
        V res = G::identity();
        for (i++ ; i ; i -= lsb(i)) res = G::operation(dat_[i], res);
        return res;
    }

    void set(usize i, V v) {
        assert(0 <= i and i < size());
        v = G::operation(G::inverse((*this)[i]), v);
        operation(i, v);
    }

    template <class F>
    std::optional<usize> maxRight(usize l, F f) const requires concepts::Predicate<F, V> {
        assert(l < size());
        V sum = l ? (*this)[l - 1] : G::identity();
        usize r = 0;
        for (u32 w = lg_ ; w <= lg_ ; w--) {
            usize next = r | (1u << w);
            if (next >= dat_.size()) continue;
            V nsum = G::operation(sum, dat_[next]);
            if (f(nsum)) {
                sum = std::move(nsum);
                r = std::move(next);
            }
        }
        assert(l <= r);
        return r == size() and f(sum) ? std::nullopt : std::optional{r};
    }

    // 実装が合いません。頭が悪いので
    // template <class F>
    // requires Concept::Predicate<F, V>
    // std::optional<usize> minLeft(usize r, F f) const {
    //     assert(r <= n_);
    //     V sum = G::identity();
    //     usize l = 0;
    //     for (u32 w = lg_ ; w <= lg_ ; w--) {
    //         u32 next = l | (1u << w);
    //         if (next >= r) continue;
    //         V nsum = G::operation(dat_[next], sum);
    //         if (!f(nsum)) {
    //             sum = std::move(nsum);
    //             l = std::move(next);
    //         }
    //     }
    //     assert(l <= r);
    //     if (l + 1 == r and !f(sum)) return r;
    //     return l == 0u and f(sum) ? std::nullopt : std::optional{l};
    // }

private:

    usize n_;

    u32 lg_;

    std::vector<V> dat_;

    constexpr i32 lsb(i32 x) const noexcept {
        return x & -x;
    }

    void add(i32 i, const V& v) {
        for (i++ ; i <= (i32)size() ; i += lsb(i)) dat_[i] = G::operation(dat_[i], v);
    }
};

} // namespace zawa
#line 2 "Src/Algebra/Group/AdditiveGroup.hpp"

namespace zawa {

template <class T>
class AdditiveGroup {
public:
    using Element = T;
    static constexpr T identity() noexcept {
        return T{};
    }
    static constexpr T operation(const T& l, const T& r) noexcept {
        return l + r;
    }
    static constexpr T inverse(const T& v) noexcept {
        return -v;
    }
};

} // namespace zawa
#line 6 "Test/LC/vertex_get_range_contour_add_on_tree.test.cpp"
using namespace zawa;

#line 9 "Test/LC/vertex_get_range_contour_add_on_tree.test.cpp"
#include <iostream>
#line 11 "Test/LC/vertex_get_range_contour_add_on_tree.test.cpp"
using namespace std;

int main() {
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    int N, Q;
    cin >> N >> Q;
    vector<int> A(N);
    for (auto& x : A)
        cin >> x;
    vector<vector<int>> G(N);
    for (int i = 0 ; i < N - 1 ; i++) {
       int u, v;
       cin >> u >> v;
       G[u].push_back(v);
       G[v].push_back(u);
    }
    ContourAggregation sol(move(G));
    cerr << "size=" << ssize(sol) << endl;
    DualFenwickTree<AdditiveGroup<long long>> fen(ssize(sol));
    for (int i = 0 ; i < N ; i++) 
        for (auto [L, R] : sol.contour(i,0))
            fen.operation(L,R,A[i]);
    while (Q--) {
        int T;
        cin >> T;
        if (T == 0) {
            int p, l, r, x;
            cin >> p >> l >> r >> x;
            for (auto [L, R] : sol.contour(p,l,r))
                fen.operation(L,R,x);
        }
        else if (T == 1) {
            int p;
            cin >> p;
            long long ans = 0;
            for (auto i : sol.point(p))
                ans += fen[i];
            cout << ans << '\n';
        }
        else
            assert(0);
    }
}
Back to top page