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 1n4×105
∣ x ∣ , ∣ y ∣ , ∣ x 0 ∣ , ∣ y 0 ∣ ≤ 1 0 8 |x|,|y|,|x_0|,|y_0|\le 10^8 x,y,x0,y0108
1 ≤ l ≤ r ≤ ∣ L ∣ 1\le l \le r\le |L| 1lrL
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} y0i=lmaxr{y0x0xi+yi},y0>0y0i=lminr{y0x0xi+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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值