BZOJ4311 向量(线段树分治+凸包)
BZOJ4311 向量(线段树分治+凸包)
题目大意
你要维护一个向量集合,支持以下操作:
-
插入一个向量 (x, y)
-
删除插入的第 i 个向量
-
查询当前集合与 (x, y) 点积的最大值是多少。如果当前是空集输出 0
数据范围
$ n \le 200000 , 1 \le x,y \le 10^6 $
解题思路
线段树分治好题
首先发现对于一个向量所在的直线,垂直于它的所有直线斜率相等,这样的直线经过向量集合中的点且最靠右的即为答案,不难发现维护向量集合的凸包即可
凸包不好合并更不好删除,考虑线段树分治,将每个向量出现的时间分为 $ log $ 段,挂在线段树上,并在线段树上每个点构建一个凸包,求解单个凸包时可以用三分,询问就是从根节点到叶节点所有答案的最小值
我的写法中提前将点按 x 排序,常数回小一点把,凸包是一个节点构建完就询问的
代码如下
#pragma GCC optimize(3, "inline")
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Pa pair<ll, ll>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }
template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }
const int N = 300500;
int cnt, cnt2, T, tp;
struct node {
ll x, y, l, r;
bool operator < (const node &i) const {
return x < i.x;
}
}vec[N], v[N];
vector<Pa > poi[N<<2];
Pa st[N];
#define ls p << 1
#define rs ls | 1
void change(int p, int l, int r, Pa x, int L, int R) {
if (L <= l && r <= R) return poi[p].push_back(x), void();
int mid = (l + r) >> 1;
if (L <= mid) change(ls, l, mid, x, L, R);
if (mid < R) change(rs, mid + 1, r, x, L, R);
}
double K(Pa a, Pa b, Pa c) {
return (a.se - b.se) * (a.fi - c.fi) < (a.se - c.se) * (a.fi - b.fi) ;
}
ll Ans(Pa a, Pa b) {
return a.fi * b.fi + a.se * b.se;
}
ll ans[N];
void solve(int p, int l, int r) {
tp = 0;
for (auto x: poi[p]) {
while (tp > 1 && K(st[tp-1], st[tp], x)) tp--;
st[++tp] = x;
}
for (int i = l;i <= r; i++) {
if (!v[i].l) continue;
Pa t = MP(v[i].x, v[i].y);
int L = 1, R = tp, ti = 0;
while (L < R) {
ti++;
int m1 = (L + R) >> 1;
if (Ans(t, st[m1]) < Ans(t, st[m1+1])) L = m1 + 1;
else R = m1;
}
Mx(ans[v[i].l], Ans(t, st[R]));
}
if (l == r) return; int mid = (l + r) >> 1;
solve(ls, l, mid), solve(rs, mid + 1, r);
}
int main() {
freopen ("hs.in","r",stdin);
freopen ("hs.out","w",stdout);
int n; read(n);
for (int i = 1;i <= n; i++) {
int op, x, y;
read(op), read(x);
if (op == 1)
read(y), vec[++cnt] = (node) { x, y, i, n };
else if (op == 2) vec[x].r = i;
else read(y), cnt2++, v[i] = (node) {x, y, cnt2, cnt2};
}
sort(vec + 1, vec + cnt + 1);
for (int i = 1;i <= cnt; i++)
change(1, 1, n, MP(vec[i].x, vec[i].y), vec[i].l, vec[i].r);
solve(1, 1, n);
for (int i = 1;i <= cnt2; i++) printf ("%lld\n", ans[i]);
return 0;
}