YBTOJ:向量问题(线段树分治、凸包)

21 篇文章 0 订阅
19 篇文章 2 订阅

题目描述

你要维护一个向量集合,支持以下操作:

插入一个向量 。
删除插入的第 x 个向量。
查询当前集合与 ( x , y ) (x,y) (x,y) 点积的最大值是多少。如果当前是空集输出0。

数据范围

n < = 2 e 5 , x 、 y ∈ [ 1 , 2 e 6 ] n<=2e5,x、y∈[1,2e6] n<=2e5,xy[1,2e6]

解析

考虑作当前向量的垂线,该线自上而下平移截得的第一个向量对应的点就是答案
如果本题没有删除操作,可以利用一个凸包来维护
但是如何支持删除呢?
考虑离线,对询问时间维护一个线段树
那么每个向量都有一个对应的有效区间
把该向量按照区间修改的方式加到线段树上,对每个线段树节点维护一个凸包
询问是从叶节点一直往上走,对每一个节点的凸包二分找到切点一直更新答案

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
const int M=2e6+100;
#define ll long long
ll read(){
	ll x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();};
	while(isdigit(c)){x=x*10+c-'0';c=getchar();};
	return x*f;
}
int n,m;
struct node{
	ll x,y;
	int id;
	bool operator < (const node o){
		if(x!=o.x) return x<o.x;
		return y<o.y;
	}
}p[N],ask[N];
int num,ans[N],cnt;
#define mid ((l+r)>>1)
#define ls (k<<1)
#define rs (k<<1|1)
#define ed (tr[k].size()-1)
vector<int>tr[N<<2];
void add(int k,int x){
	while(tr[k].size()>1&&(p[tr[k][ed]].y-p[tr[k][ed-1]].y)*(p[x].x-p[tr[k][ed]].x)<=(p[x].y-p[tr[k][ed]].y)*(p[tr[k][ed]].x-p[tr[k][ed-1]].x)) tr[k].pop_back();
	tr[k].push_back(x);
}
void insert(int k,int l,int r,int x,int y,int o){
	//printf("insert:l=%d r=%d x=%d y=%d o=%d mid=%d %d %d\n",l,r,x,y,o,mid,x<=mid,y>mid);
	if(x<=l&&r<=y){
		//printf("  add:l=%d r=%d o=%d\n",l,r,o);
		add(k,o);return;
	}
	if(x<=mid) insert(ls,l,mid,x,y,o);
	if(y>mid) insert(rs,mid+1,r,x,y,o);
}
int find(int k,ll x,ll y){
	//printf("  k=%d find: siz=%d\n",k,tr[k].size());
	//for(int i=0;i<tr[k].size();i++) printf("  (%lld %lld)",p[tr[k][i]].x,p[tr[k][i]].y);
	//printf("\n");
	if(!tr[k].size()) return -1;
	else if(tr[k].size()==1||(p[tr[k][ed]].y-p[tr[k][ed-1]].y)*y>=-x*(p[tr[k][ed]].x-p[tr[k][ed-1]].x)) return tr[k][ed];
	int l=0,r=ed;
	while(l<r){
		int o=(l+r)>>1;
		if(o==ed||(p[tr[k][o+1]].y-p[tr[k][o]].y)*y<=-x*(p[tr[k][o+1]].x-p[tr[k][o]].x)) r=o;
		else l=o+1;
		//printf("    o=%d l=%d r=%d\n",o,l,r);
	}
	return tr[k][l];
}
ll query(int k,int l,int r,int o,ll x,ll y){
	//printf("k=%d l=%d r=%d x=%lld y=%lld\n",k,l,r,x,y);
	int pl=find(k,x,y);
	ll res=pl==-1?0:x*p[pl].x+y*p[pl].y;
	if(l==r) return res;
	if(o<=mid) res=max(res,query(ls,l,mid,o,x,y));
	else res=max(res,query(rs,mid+1,r,o,x,y));
	return res;
}
int st[N],eed[N];
int main(){
	n=read();
//	for(int i=1;i<=100;i++) tr[i].resize(10);
	for(int i=1;i<=n;i++){
		int f=read();
		if(f==1){
			int x=read(),y=read();
			//printf("%d %d %d\n",f,x,y);
			p[++cnt]=(node){x,y,cnt};st[cnt]=i;eed[cnt]=n;
		}
		else if(f==2){
			int x=read();eed[x]=i-1;
			//printf("%d %d\n",f,x);
		}
		else{
			int x=read(),y=read();
			//printf("%d %d %d\n",f,x,y);
			ask[++num]=(node){x,y,i};
		}
	}
	sort(p+1,p+1+cnt);
	for(int i=1;i<=cnt;i++){
		//printf("i=%d l=%d r=%d\n",i,st[p[i].id],eed[p[i].id]);
		insert(1,1,n,st[p[i].id],eed[p[i].id],i);
	}
	for(int i=1;i<=num;i++){
		printf("%lld\n",query(1,1,n,ask[i].id,ask[i].x,ask[i].y));
	}
	return 0;
}
/*
5
3 84040 650026
3 702678 199950
1 826333 497249
1 133580 956915
3 36119 655069
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值