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


    向量问题

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

    题目大意

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

    思路

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

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

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

    当然我们还可以继续优化,就是把二分换成决策单调化。
    我们考虑以一个顺序处理询问,使得它的答案按 (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
    ", ans[i]);
    	
    	return 0;
    }
    
  • 相关阅读:
    Golang Web入门(3):如何优雅的设计中间件
    Golang Web入门(2):如何实现一个高性能的路由
    基于MySQL 的 SQL 优化总结
    Redis系列(七)Redis面试题
    Redis系列(六)Redis 的缓存穿透、缓存击穿和缓存雪崩
    Redis系列(五)发布订阅模式、主从复制和哨兵模式
    Redis系列(四)Redis配置文件和持久化
    Redis系列(三)Redis的事务和Spring Boot整合
    Redis系列(二)Redis的8种数据类型
    Redis系列(一)Redis入门
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_6-1-2.html
Copyright © 2020-2023  润新知