Skip to the content.

:x: test/yukicoder/2012.test.cpp

Depends on

Code

#define PROBLEM "https://yukicoder.me/problems/no/2012"
#include <bits/stdc++.h>

#include "library/linearalgebra/ConvexHullTrick.hpp"
#include "library/r2/XY.hpp"
using ll = long long;
using ld = long double;
void chmax(ld &a, ld b) {
    if (a < b)
        a = b;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;
    std::vector<XY<ld>> xy(n);
    for (int i = 0; i < n; i++)
        std::cin >> xy[i];
    std::ranges::sort(xy);

    MinConvexHullTrick<ld> CHT1;
    MaxConvexHullTrick<ld> CHT2;

    ld ans = 0;

    for (const auto &v : xy) {
        if (v.x == 0) {
            chmax(ans, abs(xy[0].x * v.y));
            chmax(ans, abs(xy.back().x * v.y));
            continue;
        }
        if (CHT1.size()) {
            chmax(ans, abs(CHT1.query(v.y / v.x) * v.x));
            chmax(ans, abs(CHT2.query(v.y / v.x) * v.x));
        }
        Line<ld> f(v.x, -v.y);
        CHT1.add(f);
        CHT2.add(f);
    }
    std::cout << ll(round(ans)) << '\n';
}
#line 1 "test/yukicoder/2012.test.cpp"
#define PROBLEM "https://yukicoder.me/problems/no/2012"
#include <bits/stdc++.h>

#line 2 "library/linearalgebra/Linear.hpp"
template <typename T> struct Line {
    T a, b;
    Line() = default;
    Line(T a, T b) : a(a), b(b) {}
    Line(std::pair<T, T> l) : a(l.first), b(l.second) {}
    Line(T c) : a(0), b(c) {}

    T operator()(const T x) const { return a * x + b; }
    Line operator()(const Line &l) const { return Line(a * l.a, a * l.b + b); }

    bool operator==(const Line &l) const { return a == l.a and b == l.b; }
    bool operator!=(const Line &l) const { return !(*this == l); }
    bool operator<(const Line &l) const {
        return (a == l.a ? a < l.a : b < l.b);
    }

    Line &operator+=(const Line &l) {
        a += l.a;
        b += l.b;
        return *this;
    }
    Line operator+(const Line &l) const { return Line(*this) += l; }
    Line &operator-=(const Line &l) {
        a -= l.a;
        b -= l.b;
        return *this;
    }
    Line operator-(const Line &l) const { return Line(*this) -= l; }
    Line operator-() const { return Line(-a, -b); }

    Line &operator+=(const T &c) {
        b += c;
        return *this;
    }
    Line operator+(const T &c) const { return Line(*this) += c; }
    Line &operator-=(const T &c) {
        b -= c;
        return *this;
    }
    Line operator-(const T &c) const { return Line(*this) -= c; }
    Line &operator*=(const T &c) {
        a *= c;
        b *= c;
        return *this;
    }
    Line operator*(const T &c) const { return Line(*this) *= c; }
    Line &operator/=(const T &c) {
        a /= c;
        b /= c;
        return *this;
    }
    Line operator/(const T &c) const { return Line(*this) /= c; }

    Line inv() const {
        assert(a != 0);
        return Line(T(1) / a, -b / a);
    }

    friend std::istream &operator>>(std::istream &is, Line &l) {
        is >> l.a >> l.b;
        return is;
    }
    friend std::ostream &operator<<(std::ostream &os, const Line &l) {
        if (l.a == 0 and l.b == 0)
            os << 0;
        else if (l.a == 0)
            os << l.b;
        else if (l.b == 0)
            os << l.a << "x";
        else if (l.b > 0)
            os << l.a << "x+" << l.b;
        else
            os << l.a << "x-" << -l.b;
        return os;
    }
};
#line 3 "library/linearalgebra/ConvexHullTrick.hpp"
namespace convex_hull_trick {
enum Objective {
    MINIMIZE = +1,
    MAXIMIZE = -1,
};

// 最大化の場合は反転して、内部では最小化問題のみを扱う
// 傾きが狭義単調減少になるように保存

template <typename T, Objective OBJ>
class ConvexHullTrick : std::deque<Line<T>> {
    using L = Line<T>;
    using std::deque<L>::push_back;
    using std::deque<L>::push_front;
    using std::deque<L>::pop_back;
    using std::deque<L>::pop_front;

    const L& at(int i) const { 
        return i >= 0 ? std::deque<L>::at(i) : std::deque<L>::at(size() + i);
    }

    static bool check(const L &l1, const L &l2, const L &l3) {
        // l2 が要らないなら true を返す
        // 傾きが狭義単調減少は保証されてる
        // 交点の x 座標は (l2.b-l1.b)/(l1.a-l2.a) と (l3.b-l2.b)/(l2.a-l3.a)
        // これが >= だと要らない
        return (l2.b - l1.b) * (l2.a - l3.a) >= (l3.b - l2.b) * (l1.a - l2.a);
    }

    static T apply_objective(const T &value) {
        if constexpr (OBJ == Objective::MINIMIZE)
            return value;
        else
            return -value;
    }

    void internal_push_back(const L &l) {
        assert(!size() or at(-1).a >= l.a);

        if(size() and at(-1).a == l.a) 
            if(at(-1).b <= l.b)
                return;
            else
                pop_back();
        
        while (size() >= 2 and check(at(-2), at(-1), l)) 
            pop_back();
        
        push_back(l);
    }

    void internal_push_front(const L &l) {
        assert(!size() or l.a >= at(0).a);

        if(size() and at(0).a == l.a)
            if(at(0).b <= l.b)
                return;
            else
                pop_front();

        while (size() >= 2 and check(l, at(0), at(1)))
            pop_front();
        
        push_front(l);
    }
  public:
    using value_type = L;
    using std::deque<L>::size;

    ConvexHullTrick() = default;
    ConvexHullTrick(std::vector<L> lines) {
        if (OBJ == -1)
            for (auto &l : lines)
                l = -l;
        std::ranges::sort(lines);
        for (const auto &l : lines)
            internal_push_back(l);
    }

    void add(L l) {
        if (OBJ == -1)
            l = -l;
        if (!size() or at(-1).a >= l.a)
            internal_push_back(l);
        else if (l.a >= at(0).a)
            internal_push_front(l);
        else
            assert(false);
    }

    void add(T a, T b) { add(L(a, b)); }

    // return min_f{f(x)}
    T query(T x) const {
        assert(size());
        int l = -1, r = size() - 1;
        while (r - l > 1) {
            int m = (l + r) >> 1;
            (at(m)(x) >= at(m + 1)(x) ? l : r) = m;
        }
        return apply_objective(at(r)(x));
    }

    // return min_f{f(x)}
    // 任意の X<x に対して f = argmin_f{f(X)} とならない f を全て削除する
    T query_monotone_inc(T x) {
        assert(size());
        while (size() >= 2 and at(0)(x) >= at(1)(x))
            pop_front();
        return apply_objective(at(0)(x));
    }

    // return min_f{f(x)}
    // 任意の X>x に対して f = argmin_f{f(X)} とならない f を全て削除する
    T query_monotone_dec(T x) {
        assert(size());
        while (size() >= 2 and at(-2)(x) <= at(-1)(x))
            pop_back();
        return apply_objective(at(-1)(x));
    }

    std::vector<T> query(const std::vector<T> &xs) {
        int n = xs.size();
        std::vector<int> idx(n);
        std::iota(idx.begin(), idx.end(), 0);
        std::ranges::sort(idx, std::ranges::less{}, [&](int i) { return xs[i]; });
        std::vector<T> ans(n);
        for (int id : idx)
            ans[id] = query_monotone_inc(xs[id]); 
        return ans;
    }

    friend std::ostream &operator<<(std::ostream &os,
                                    const ConvexHullTrick &cht) {
        os << "[";
        for (int i = 0; i < cht.size(); i++)
            os << (OBJ == -1 ? -cht.at(i) : cht.at(i))
               << "],"[i + 1 < cht.size()];
        if (!cht.size())
            os << "]";
        return os;
    }
};
} // namespace convex_hull_trick
template <typename T>
using MinConvexHullTrick =
    convex_hull_trick::ConvexHullTrick<T,
                                       convex_hull_trick::Objective::MINIMIZE>;
template <typename T>
using MaxConvexHullTrick =
    convex_hull_trick::ConvexHullTrick<T,
                                       convex_hull_trick::Objective::MAXIMIZE>;
#line 2 "library/r2/XY.hpp"
template <typename T> struct XY {
    T x, y;
    XY() = default;
    XY(T x, T y) : x(x), y(y) {}
    XY(const std::pair<T, T> &xy) : x(xy.first), y(xy.second) {}

    XY operator+() const { return *this; }
    XY operator-() const { return XY(-x, -y); }

    XY &operator++() {
        x++;
        y++;
        return *this;
    }
    XY &operator--() {
        x--;
        y--;
        return *this;
    }
    XY &operator++(int) {
        XY a = *this;
        ++*this;
        return a;
    }
    XY &operator--(int) {
        XY a = *this;
        --*this;
        return a;
    }

    XY &operator+=(const XY &v) {
        x += v.x;
        y += v.y;
        return *this;
    }
    XY &operator-=(const XY &v) {
        x -= v.x;
        y -= v.y;
        return *this;
    }
    XY &operator*=(const T &a) {
        x *= a;
        y *= a;
        return *this;
    }
    XY &operator/=(const T &a) {
        x /= a;
        y /= a;
        return *this;
    }

    friend XY operator+(const XY &u, const XY &v) { return XY(u) += v; }
    friend XY operator-(const XY &u, const XY &v) { return XY(u) -= v; }
    friend XY operator*(const XY &u, const T &a) { return XY(u) *= a; }
    friend XY operator*(const T &a, const XY &u) { return XY(u) *= a; }
    friend XY operator/(const XY &u, const T &a) { return XY(u) /= a; }

    bool operator<(const XY &v) const { return x != v.x ? x < v.x : y < v.y; }
    bool operator>(const XY &v) const { return x != v.x ? x > v.x : y > v.y; }
    bool operator==(const XY &v) const { return x == v.x and y == v.y; }
    bool operator<=(const XY &v) const { return !(*this > v); }
    bool operator>=(const XY &v) const { return !(*this < v); }
    bool operator!=(const XY &v) const { return !(*this == v); }

    double arg() const { return atan2(y, x); }

    // [0,2pi) で θ(u)<θ(v) の時 true
    // (0,0) は 2pi に相当
    // static bool angle_cmp(const XY&u,const XY&v){
    //  using U=conditional_t< is_same_v<T,int>,long long,T>;
    //  if(u==XY(0,0))return false;
    //  if(v==XY(0,0))return true;
    //  if(u.y==0){
    //    if(u.x>0)return true;
    //    if(v.y==0)return v.x<0;
    //    return v.y<0;
    //  }
    //  if(u.y>0){
    //    if(v.y==0)return v.x<0;
    //    if(v.y<0)return true;
    //    return U(v.x)*u.y <= U(u.x)*v.y;
    //  }
    //  if(v.y>=0)return false;
    //  return U(v.x)*u.y <= U(u.x)*v.y;
    //}

    friend T dot(const XY &u, const XY &v) { return u.x * v.x + u.y * v.y; }
    T norm() { return dot(*this, *this); }
    T abs() { return sqrt(norm()); }

    friend std::istream &operator>>(std::istream &is, XY &v) {
        is >> v.x >> v.y;
        return is;
    }
    friend std::ostream &operator<<(std::ostream &os, const XY &v) {
        os << v.x << " " << v.y;
        return os;
    }

    static XY direction(const char &c) {
        if (c == 'R')
            return {1, 0};
        if (c == 'L')
            return {-1, 0};
        if (c == 'U')
            return {0, -1};
        if (c == 'D')
            return {0, 1};
        return {0, 0};
    }
};
#line 6 "test/yukicoder/2012.test.cpp"
using ll = long long;
using ld = long double;
void chmax(ld &a, ld b) {
    if (a < b)
        a = b;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;
    std::vector<XY<ld>> xy(n);
    for (int i = 0; i < n; i++)
        std::cin >> xy[i];
    std::ranges::sort(xy);

    MinConvexHullTrick<ld> CHT1;
    MaxConvexHullTrick<ld> CHT2;

    ld ans = 0;

    for (const auto &v : xy) {
        if (v.x == 0) {
            chmax(ans, abs(xy[0].x * v.y));
            chmax(ans, abs(xy.back().x * v.y));
            continue;
        }
        if (CHT1.size()) {
            chmax(ans, abs(CHT1.query(v.y / v.x) * v.x));
            chmax(ans, abs(CHT2.query(v.y / v.x) * v.x));
        }
        Line<ld> f(v.x, -v.y);
        CHT1.add(f);
        CHT2.add(f);
    }
    std::cout << ll(round(ans)) << '\n';
}
Back to top page