#include<bits/stdc++.h> #define ll long long /// NOTE: // This is MIN-optimizing struct ConvexHullTrick { struct Line { int id; ll a, b; double control_min; // control_min Line(ll _1, ll _2, ll _3, double _4) { id = _1; a = _2; b = _3; control_min = _4; } // to change to MAX-optimizer, change ONLY this method: // replace the 2 '>' to '<' bool operator < (Line l) const { if (a != l.a) return a > l.a; return b > l.b; } }; ll inf; vector<Line> lines; set<Line> bag; set<pair<double, int> > result; ConvexHullTrick() { inf = 1000000009ll * 1000000009ll; } void init() { lines.clear(); bag.clear(); result.clear(); } double get_x_intersect(Line l1, Line l2) { return 1. * (l1.b - l2.b) / (l2.a - l1.a); } void addLine(ll a, ll b) { // add line y = ax + b Line line(lines.size(), a, b, -inf); lines.push_back(line); // the first line if (bag.empty()) { bag.insert(line); result.insert({-inf, line.id}); return; } // if bag contains a line with the same slope and smaller-or-equal intercept // or if line is useless auto it = bag.lower_bound(line); if (it->a == line.a) return; if (it != bag.begin() && it != bag.end()) { auto before = it; before--; if (get_x_intersect(*before, *it) <= get_x_intersect(*before, line)) return; } // normal case: line is useful // first: remove forward-useless lines if (it != bag.begin()) { it--; while(it != bag.begin()) { auto before = it; before--; if (get_x_intersect(*before, line) <= get_x_intersect(*before, *it)) { result.erase({it->control_min, it->id}); bag.erase(it); it = before; } else break; } line.control_min = get_x_intersect(line, *it); } else { line.control_min = -inf; } // second: add line result.insert({line.control_min, line.id}); auto line_it = bag.insert(line).first; // third: remove backward-useless lines it = line_it; it++; while(it != bag.end()) { auto next = it; next++; if (next == bag.end()) break; if (get_x_intersect(line, *next) <= get_x_intersect(line, *it)) { Line lnext = *next; lnext.control_min = get_x_intersect(line, *next); result.erase({next->control_min, next->id}); result.insert({lnext.control_min, lnext.id}); bag.erase(next); bag.insert(lnext); result.erase({it->control_min, it-> id}); it = bag.erase(it); } else break; } // update the line to the right of current line if (it != bag.end()) { Line lit = *it; lit.control_min = get_x_intersect(line, lit); result.erase({it->control_min, it->id}); result.insert({lit.control_min, lit.id}); bag.erase(it); bag.insert(lit); } } pair<ll, int> query(ll x) { auto it = result.upper_bound({x, -1}); it--; ll best_value = 1ll * lines[it->second].a * x + lines[it->second].b; return {best_value, it->second}; // {best value, index of the best line} } };