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
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;
}