[BZOJ4311]向量

Posted by Dispwnl on December 9, 2018

题目

Description

你要维护一个向量集合,支持以下操作: 1.插入一个向量(x,y) 2.删除插入的第i个向量 3.查询当前集合与(x,y)点积的最大值是多少。如果当前是空集输出0

Input

第一行输入一个整数n,表示操作个数 接下来n行,每行先是一个整数t表示类型,如果t=1,输入向量 (x,y);如果t=2,输入id表示删除第id个向量;否则输入(x,y),查询 与向量(x,y)点积最大值是多少。 保证一个向量只会被删除一次,不会删没有插入过的向量

Output

对于每条t=3的询问,输出一个答案

Sample Input

5
1 3 3
1 1 4
3 3 3
2 1
3 3 3

Sample Output

18
15

HINT

n<=200000 1<=x,y<=10^6

题解

每个向量出现的时间是一段区间,所以可以用线段树分治搞

$(x_1,y_1),(x_2,y_2)$点积是$x_1x_2+y_1y_2$

如果对$(x_1,y_1),(x_2,y_2)$查询向量$(x,y)$,假设$(x_1,y_1)$点积更大,则有式子$xx_1+yy_1>xx_2+yy_2$

即$x(x_1-x_2)>y(y_2-y_1)$

即$-\frac{x}{y}>\frac{y_1-y_2}{x_1-x_2}$

右边的是个斜率,左边的是直线$y=kx$的垂线

所以维护斜率凸壳即可

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<vector>
# include<algorithm>
# define tl (k<<1)
# define tr (k<<1|1)
# define mid (l+r>>1)
# define LL long long
using namespace std;
const int MAX=2e5+5;
struct p{
    int x,y,l,r;
    bool operator< (const p &a)
    const{
        if(x!=a.x) return x<a.x;
        return y<a.y;
    }
}qu[MAX];
struct q{
    int x,y;
}qwq[MAX],st[MAX];
int n,cnt,T,top;
int L[MAX];
LL ans[MAX];
vector<int> s[MAX<<2];
int read()
{
    int x(0);
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar());
    for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
    return x;
}
void change(int l,int r,int k,int L,int R,int id)
{
    if(L>R) return;
    if(l==L&&r==R) return void(s[k].push_back(id));
    if(R<=mid) change(l,mid,tl,L,R,id);
    else if(L>mid) change(mid+1,r,tr,L,R,id);
    else change(l,mid,tl,L,mid,id),change(mid+1,r,tr,mid+1,R,id);
}
LL GET_NUM(q a,q b)
{
    return 1ll*a.x*b.x+1ll*a.y*b.y;
}
LL calc(q a,q b,q c)
{
    return 1ll*(a.x-c.x)*(b.y-c.y)-1ll*(a.y-c.y)*(b.x-c.x);
}
LL GET_ANS(int x)
{
    int l=1,r=top;
    while(l+3<=r)
    {
        int mid1=(l+r>>1),mid2=(mid1+r>>1);
        if(GET_NUM(qwq[x],st[mid1])<=GET_NUM(qwq[x],st[mid2])) l=mid1;
        else r=mid2;
    }
    LL ans=0;
    for(int i=l;i<=r;++i)
      ans=max(ans,GET_NUM(qwq[x],st[i]));
    return ans;
}
void Solve(int l=1,int r=n,int k=1)
{
    top=0;
    for(int i=0,cnt=s[k].size();i<cnt;++i)
      {
        p tt=qu[s[k][i]];
        while(top>1&&calc(st[top-1],st[top],(q){tt.x,tt.y})>=0) --top;
        st[++top]=(q){tt.x,tt.y};
      }
    if(s[k].size()) for(int i=l;i<=r;++i)
      if(qwq[i].x) ans[i]=max(ans[i],GET_ANS(i));
    if(l==r) return;
    Solve(l,mid,tl),Solve(mid+1,r,tr);
}
int main()
{
    n=read();
    memset(ans,-1,sizeof(ans));
	for(int i=1,op,x,y;i<=n;++i)
      {
        op=read(),x=read();
        if(op==1) qu[++cnt]=(p){x,read()},L[cnt]=i;
        else if(op==2) qu[x].l=L[x],qu[x].r=i-1,L[x]=0;
        else if(op==3) qwq[i]=(q){x,read()},ans[i]=0;
      }
    for(int i=1;i<=cnt;++i)
      if(L[i]) qu[i].l=L[i],qu[i].r=n;
    sort(qu+1,qu+1+cnt);
    for(int i=1;i<=cnt;++i)
      change(1,n,1,qu[i].l,qu[i].r,i);
    Solve();
    for(int i=1;i<=n;++i)
      if(ans[i]!=-1) printf("%lld\n",ans[i]);
    return 0;
}