P3309 [SDOI2014] 向量集 Solution
Description
有一个向量列表,初始为空,有 n n n 个操作分两种:
- add ( x , y ) \operatorname{add}(x,y) add(x,y):将向量 ( x , y ) (x,y) (x,y) 添加到列表末尾.
- query ( x 0 , y 0 , l , r ) \operatorname{query}(x_0,y_0,l,r) query(x0,y0,l,r):求 max i = l r { ( x 0 , y 0 ) ⋅ ( x i , y i ) } \max\limits_{i=l}^r\{(x_0,y_0)\cdot(x_i,y_i)\} i=lmaxr{(x0,y0)⋅(xi,yi)}.
Limitations
本题强制在线
1 ≤ n ≤ 4 × 1 0 5 1\le n\le 4\times 10^5 1≤n≤4×105
∣ x ∣ , ∣ y ∣ , ∣ x 0 ∣ , ∣ y 0 ∣ ≤ 1 0 8 |x|,|y|,|x_0|,|y_0|\le 10^8 ∣x∣,∣y∣,∣x0∣,∣y0∣≤108
1 ≤ l ≤ r ≤ ∣ L ∣ 1\le l \le r\le |L| 1≤l≤r≤∣L∣
2 s , 500 MB 2\text{s},500\text{MB} 2s,500MB
Solution
原式可写为:
{ y 0 ⋅ max i = l r { x 0 y 0 ⋅ x i + y i } , y 0 > 0 y 0 ⋅ min i = l r { x 0 y 0 ⋅ x i + y i } , y 0 < 0 \begin{cases} y_0\cdot\max\limits_{i=l}^r\{\dfrac{x_0}{y_0}\cdot x_i+y_i\},\quad y_0 > 0\\ y_0\cdot\min\limits_{i=l}^r\{\dfrac{x_0}{y_0}\cdot x_i+y_i\},\quad y_0 < 0\\ \end{cases} ⎩ ⎨ ⎧y0⋅i=lmaxr{y0x0⋅xi+yi},y0>0y0⋅i=lminr{y0x0⋅xi+yi},y0<0
这表明答案在上下凸壳上.
由于强制在线,且修改是从左到右的,有一个简单做法.
我们使用线段树,在节点的 r r r 被修改到时,对 [ l , r ] [l,r] [l,r] 内的点排序,并建出上下凸壳,询问直接在每个节点的凸壳上三分即可.
由于每个点只会被修改一次,且未修改的点无法访问到,算法正确性显然.
建凸壳的时间复杂度为 O ( n ) O(n) O(n),一次修改要建 O ( log n ) O(\log n) O(logn) 个,故总时间复杂度为 O ( n log 2 n ) O(n\log^2n) O(nlog2n).
Code
4.22 KB , 3.02 s , 95.54 MB (in total, C++20 with O2) 4.22\text{KB},3.02\text{s},95.54\text{MB}\;\texttt{(in total, C++20 with O2)} 4.22KB,3.02s,95.54MB(in total, C++20 with O2)
// Problem: P3309 [SDOI2014] 向量集
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3309
// Memory Limit: 500 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;template<class T>
bool chmax(T &a, const T &b){if(a < b){ a = b; return true; }return false;
}template<class T>
bool chmin(T &a, const T &b){if(a > b){ a = b; return true; }return false;
}constexpr i64 inf = 1e18;namespace hull {struct Point {int x, y;inline Point(int _x = 0, int _y = 0): x(_x), y(_y) {}};inline bool operator<(const Point& a, const Point& b) {return (a.x == b.x ? a.y < b.y : a.x < b.x);}inline i64 operator*(const Point& a, const Point& b) {return 1LL * a.x * b.y - 1LL * a.y * b.x;}inline Point operator-(const Point& a, const Point& b) {return Point(a.x - b.x, a.y - b.y);}inline i64 dot(const Point& a, const Point& b) {return 1LL * a.x * b.x + 1LL * a.y * b.y;}struct Hull {vector<Point> pts;int tp;inline Hull() {}inline Hull(int _tp) : tp(_tp) {}Point& lst() { return pts[pts.size() - 1]; }Point& lst2() { return pts[pts.size() - 2]; }inline void push_back(const Point& it) {auto check = [&](i64 x) { return (tp ? x >= 0 : x <= 0); };while (pts.size() >= 2 && check((it - lst2()) * (lst() - lst2()))) {pts.pop_back();}pts.push_back(it);}inline i64 find(const Point& a) {int l = 0, r = pts.size() - 1;while (r - l >= 3) {const int lm = l + (r - l) / 3, rm = r - (r - l) / 3;if (dot(pts[lm], a) > dot(pts[rm], a)) r = rm;else l = lm;}i64 ans = -inf;for (int i = l; i <= r; i++) ans = max(ans, dot(pts[i], a));return ans;}};
}using hull::Point;
using hull::Hull;namespace seg_tree {struct Node {int l, r;Hull up, down;};inline int ls(int u) { return u * 2 + 1; }inline int rs(int u) { return u * 2 + 2; }struct SegTree {vector<Point> pts;vector<Node> tr;inline SegTree() {}inline SegTree(int n) : tr(n << 1) { build(0, 0, n - 1); }void build(int u, int l, int r) {tr[u].l = l, tr[u].r = r;tr[u].up = Hull(0);tr[u].down = Hull(1);if (l == r) return;const int mid = (l + r) >> 1;build(ls(mid), l, mid);build(rs(mid), mid + 1, r);}void modify(int u, int p) {if (p == tr[u].r) {const int len = tr[u].r - tr[u].l + 1;vector<Point> tmp(len);copy(pts.begin() + tr[u].l, pts.begin() + tr[u].r + 1, tmp.begin());sort(tmp.begin(), tmp.end());for (auto it : tmp) {tr[u].up.push_back(it);tr[u].down.push_back(it);}}if (tr[u].l == tr[u].r) return;const int mid = (tr[u].l + tr[u].r) >> 1;if (p <= mid) modify(ls(mid), p);else modify(rs(mid), p);}i64 query(int u, int l, int r, const Point& a) {if (tr[u].l >= l && tr[u].r <= r) {if (a.y > 0) return tr[u].up.find(a);else return tr[u].down.find(a);}i64 ans = -inf;const int mid = (tr[u].l + tr[u].r) >> 1;if (l <= mid) ans = max(ans, query(ls(mid), l, r, a));if (r > mid) ans = max(ans, query(rs(mid), l, r, a));return ans;}inline void push_back(const Point& w) {pts.push_back(w);modify(0, pts.size() - 1);}inline i64 find(int l, int r, const Point& w) {return query(0, l, r, w);}};
}
using seg_tree::SegTree;signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);int n; char t;cin >> n >> t;i64 lst = 0;auto get = [&](int o) {return t == 'E' ? o : (int)(o ^ (lst & 0x7fffffff));};SegTree vec(n);for (int i = 0; i < n; i++) {char op; Point w;cin >> op >> w.x >> w.y;w.x = get(w.x); w.y = get(w.y);if (op == 'A') vec.push_back(w);else {int l, r; cin >> l >> r;l = get(l) - 1; r = get(r) - 1;cout << (lst = vec.find(l, r, w)) << endl;}}return 0;
}