题目描述
你要维护一个向量集合,支持以下操作:
插入一个向量 。
删除插入的第 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,x、y∈[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
*/