【ybt金牌导航6-1-2】向量问题

向量问题

题目链接:ybt金牌导航6-1-2

题目大意

要你支持一些操作:
加一个向量,删除第 i 个向量,给一个向量问当前有的哪个向量与它的点积最大。
如果是当前没有向量输出 0,否则输出这个最大点积。

思路

先看操作 3,设给出询问的向量是 a,b,你要找到一组原有的 x,y,使得 ax+by 最大。
那设这个最大值是 c,那就有 c=ax+by,移项搞搞什么的就得到了:y=a/bx+c/b
容易看到当 c 最大时,这个直线的截距就最大。那向量都是在第一象限,所以答案一定是在上凸包上。
那我们可以考虑在上凸壳上二分或者搞什么决策单调化什么的。

但还有一个问题,它有插入删除操作。
那就是说,它向量它只会在一个时间段里出现。
那我们考虑以时间为下标建线段树,然后插入就区间插入,到时查询就跑查询到它这个时间的链,然后链中的每个点的凸包都求一次最大值,然后把这些最大值再取最大值。
而这个线段树以时间为下标,它就是线段树分治。

那你在上凸壳上二分一个 log,线段树一个 log,复杂度就是 O(nlog2n)

当然我们还可以继续优化,就是把二分换成决策单调化。
我们考虑以一个顺序处理询问,使得它的答案按 x 坐标单调不减,那我们的最右决策点只要不停右移就能找到。
不难想到我们把询问按 a/b 从大到小排即可。

代码

#include<cstdio> #include<vector> #include<iostream> #include<algorithm> #define ll long long using namespace std; struct xl { int x, y; }; struct rd { int l, r; xl p; }a[200001], b[200001], c[200001]; int n, op, num[200001], x, qn, wn, pl[800001]; ll ans[200001]; vector <xl> v[800001]; xl operator -(xl x, xl y) {//向量减法 return (xl){x.x - y.x, x.y - y.y}; } ll dot(xl x, xl y) {//点积 return 1ll * x.x * y.x + 1ll * x.y * y.y; } ll cross(xl x, xl y) {//叉积 return 1ll * x.x * y.y - 1ll * x.y * y.x; } bool cmp1(rd x, rd y) {//按 x 排序 if (x.p.x != y.p.x) return x.p.x < y.p.x; return x.p.y < y.p.y; } bool cmp2(rd x, rd y) {//按 -a/b 从大到小排,使得最优决策点按 x 坐标单调不降 return 1ll * x.p.x * y.p.y < 1ll * y.p.x * x.p.y; } //线段树操作 void insert(int now, int l, int r, int L, int R, xl &p) { if (L <= l && r <= R) { while (v[now].size() >= 2 && cross(p - v[now][v[now].size() - 1], p - v[now][v[now].size() - 2]) <= 0) v[now].pop_back();//维护上凸包 v[now].push_back(p); return ; } int mid = (l + r) >> 1; if (L <= mid) insert(now << 1, l, mid, L, R, p); if (mid < R) insert(now << 1 | 1, mid + 1, r, L, R, p); } ll query(int now, int l, int r, int pla, xl &p) { ll ans = 0; if (v[now].size()) {//在路上的每个集合都要找上凸包 while (pl[now] < v[now].size() - 1 && dot(p, v[now][pl[now] + 1]) >= dot(p, v[now][pl[now]])) pl[now]++; ans = dot(p, v[now][pl[now]]); } if (l == r) return ans; int mid = (l + r) >> 1; if (pla <= mid) return max(ans, query(now << 1, l, mid, pla, p));//然后取最大值 else return max(ans, query(now << 1 | 1, mid + 1, r, pla, p)); } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &op); if (op == 1) { scanf("%d %d", &a[i].p.x, &a[i].p.y); a[i].l = i; a[i].r = n; num[++num[0]] = i; continue; } if (op == 2) { scanf("%d", &x); a[num[x]].r = i - 1; a[i].r = -114514;//只是标记,到时好区分操作二操作三 continue; } if (op == 3) { qn++; scanf("%d %d", &c[qn].p.x, &c[qn].p.y); c[qn].l = i; continue; } } for (int i = 1; i <= n; i++) if (a[i].l) b[++wn] = a[i]; sort(b + 1, b + wn + 1, cmp1); for (int i = 1; i <= wn; i++) insert(1, 1, n, b[i].l, b[i].r, b[i].p); for (int i = 1; i <= qn; i++) ans[c[i].l] = query(1, 1, n, c[i].l, c[i].p); for (int i = 1; i <= n; i++) if (!a[i].r) printf("%lld\n", ans[i]); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_6-1-2.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(43)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示