sotanishy's competitive programming library

sotanishy's code snippets for competitive programming

View the Project on GitHub sotanishy/cp-library-cpp

:warning: Slope Trick
(data-structure/slope_trick.hpp)

Description

Slope Trick は,区分線形凸関数 $f: T \mapsto T$ を管理するテクニックである.傾きの変化点を,傾きが正である部分と負である部分で別々に優先度付きキューで管理することで,この関数に対する様々な操作を高速に処理する.

空間計算量: $O(n)$

Operations

Note

slope trick では,

となる.凸共役を取ることで,

にできる.(参考文献参照)

Reference

Code

#pragma once
#include <functional>
#include <limits>
#include <queue>
#include <vector>

template <typename T>
class SlopeTrick {
   public:
    SlopeTrick() {
        const T INF = std::numeric_limits<T>::max() / 2;
        left.push(-INF);
        right.push(INF);
    }

    void add_const(T a) { min_y += a; }

    void add_x_minus_a(T a) {
        T l = top_l();
        if (l < a) {
            push_r(a);
        } else {
            min_y += l - a;
            push_l(a);
            push_r(pop_l());
        }
    }

    void add_a_minus_x(T a) {
        T r = top_r();
        if (a < r) {
            push_l(a);
        } else {
            min_y += a - r;
            push_r(a);
            push_l(pop_r());
        }
    }

    void add_abs(T a) {
        add_x_minus_a(a);
        add_a_minus_x(a);
    }

    void clear_left() { left.clear(); }
    void clear_right() { right.clear(); }

    void shift(T a) { shift(a, a); }

    void shift(T a, T b) {
        add_l += a;
        add_r += b;
    }

    T get_min() { return min_y; }

    T get(T x) {
        T ret = min_y;
        std::priority_queue<T, std::vector<T>> tmp_l;
        std::priority_queue<T, std::vector<T>, std::greater<T>> tmp_r;
        while (!left.empty()) {
            T a = pop_l();
            ret += max(T(0), a - x);
            tmp_l.push(a);
        }
        while (!right.empty()) {
            T a = pop_r();
            ret += max(T(0), x - a);
            tmp_r.push(a);
        }
        left.swap(tmp_l);
        right.swap(tmp_r);
        add_l = add_r = 0;
        return ret;
    }

   private:
    T min_y = 0, add_l = 0, add_r = 0;
    std::priority_queue<T, std::vector<T>> left;
    std::priority_queue<T, std::vector<T>, std::greater<T>> right;

    void push_l(T a) { left.push(a - add_l); }
    void push_r(T a) { right.push(a - add_r); }

    T top_l() { return left.top() + add_l; }
    T top_r() { return right.top() + add_r; }

    T pop_l() {
        T a = top_l();
        left.pop();
        return a;
    }

    T pop_r() {
        T a = top_r();
        right.pop();
        return a;
    }
};
#line 2 "data-structure/slope_trick.hpp"
#include <functional>
#include <limits>
#include <queue>
#include <vector>

template <typename T>
class SlopeTrick {
   public:
    SlopeTrick() {
        const T INF = std::numeric_limits<T>::max() / 2;
        left.push(-INF);
        right.push(INF);
    }

    void add_const(T a) { min_y += a; }

    void add_x_minus_a(T a) {
        T l = top_l();
        if (l < a) {
            push_r(a);
        } else {
            min_y += l - a;
            push_l(a);
            push_r(pop_l());
        }
    }

    void add_a_minus_x(T a) {
        T r = top_r();
        if (a < r) {
            push_l(a);
        } else {
            min_y += a - r;
            push_r(a);
            push_l(pop_r());
        }
    }

    void add_abs(T a) {
        add_x_minus_a(a);
        add_a_minus_x(a);
    }

    void clear_left() { left.clear(); }
    void clear_right() { right.clear(); }

    void shift(T a) { shift(a, a); }

    void shift(T a, T b) {
        add_l += a;
        add_r += b;
    }

    T get_min() { return min_y; }

    T get(T x) {
        T ret = min_y;
        std::priority_queue<T, std::vector<T>> tmp_l;
        std::priority_queue<T, std::vector<T>, std::greater<T>> tmp_r;
        while (!left.empty()) {
            T a = pop_l();
            ret += max(T(0), a - x);
            tmp_l.push(a);
        }
        while (!right.empty()) {
            T a = pop_r();
            ret += max(T(0), x - a);
            tmp_r.push(a);
        }
        left.swap(tmp_l);
        right.swap(tmp_r);
        add_l = add_r = 0;
        return ret;
    }

   private:
    T min_y = 0, add_l = 0, add_r = 0;
    std::priority_queue<T, std::vector<T>> left;
    std::priority_queue<T, std::vector<T>, std::greater<T>> right;

    void push_l(T a) { left.push(a - add_l); }
    void push_r(T a) { right.push(a - add_r); }

    T top_l() { return left.top() + add_l; }
    T top_r() { return right.top() + add_r; }

    T pop_l() {
        T a = top_l();
        left.pop();
        return a;
    }

    T pop_r() {
        T a = top_r();
        right.pop();
        return a;
    }
};
Back to top page