bzoj 4311: 向量 — 线段树分治+凸包
Contents
4311: 向量
Time Limit: 20 Sec Memory Limit: 512 MB
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
1 3 3
1 1 4
3 3 3
2 1
3 3 3
Sample Output
18
15
15
HINT
n<=200000 1<=x,y<=10^6
Source
姜大爷讲完,感觉题解好巧妙
首先我们对于删除操作可以用线段树分治来实现
然后问题的怎么查询
对于一个向量的最优值,是在与该向量垂直的线中,在距离最远的线上的点的点积
所以我们可以维护出凸包,然后在凸包上三分
那么,我们第一种想法是线段树分治时合并凸包,或平衡树维护凸包?
但是这个太难了,作为蒟蒻我当然不会了qaq
所以我们可以在线段树上每一个点维护一个凸包,在查询的时候去找从根到叶子节点路径上每一个的最优值去取一个max就好啦
最后时间复杂度O(nlog²n)
#include<map>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 1000000007
#define ll long long
#define N 200010
inline int rd()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct qaz{int x,y,l,r;}e[N],ak[N];
ll cj(int x,int y,int xx,int yy){return (ll)x*yy-(ll)y*xx;}
int n,tot,ta,xmn,ymn;
bool cmp(qaz a,qaz b)
{
return cj(a.x-xmn,a.y-ymn,b.x-xmn,b.y-ymn)>0;
}
vector<int>st[N<<2],tb[N<<2];
#define ls p<<1
#define rs p<<1|1
void add(int p,int l,int r,int L,int R,int x)
{
if(l==L&&r==R){st[p].push_back(x);return;}
int mid=l+r>>1;
if(R<=mid) add(ls,l,mid,L,R,x);
else if(L>mid) add(rs,mid+1,r,L,R,x);
else add(ls,l,mid,L,mid,x),add(rs,mid+1,r,mid+1,R,x);
}
int q[N];
bool ck(int p,int a,int b,int c)
{
int X1,Y1,X2,Y2;
X1=e[st[p][b]].x-e[st[p][c]].x;
X2=e[st[p][a]].x-e[st[p][c]].x;
Y1=e[st[p][b]].y-e[st[p][c]].y;
Y2=e[st[p][a]].y-e[st[p][c]].y;
return cj(X1,Y1,X2,Y2)<0;
}
void build(int p,int l,int r)
{
if(st[p].size()<=2) tb[p]=st[p];
else
{
q[0]=0;q[1]=1;int r=1;
st[p].push_back(st[p][0]);
for(int i=2;i<st[p].size();i++)
{
while(r&&ck(p,i,q[r],q[r-1])) r--;
q[++r]=i;
}
for(int i=0;i<=r;i++) tb[p].push_back(st[p][q[i]]);
}
if(l==r) return;
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
ll ans;
ll Dot(qaz a,qaz b){return (ll)a.x*b.x+(ll)a.y*b.y;}
void fd(int p,int l,int r,qaz c)
{
int L=0,R=tb[p].size()-1,m1,m2;
while(L+3<=R)
{
m1=(L+L+R)/3;m2=(L+R+R)/3;
if(Dot(e[tb[p][m1]],c)<=Dot(e[tb[p][m2]],c)) L=m1;
else R=m2;
}
for(int i=L;i<=R;++i)ans=max(ans,Dot(e[tb[p][i]],c));
if(l==r) return;
int mid=l+r>>1;
if(c.l<=mid) fd(ls,l,mid,c);
else fd(rs,mid+1,r,c);
}
void ask(int x)
{
ans=0;
fd(1,1,n,ak[x]);
printf("%lld\n",ans);
}
int main()
{
n=rd();
int x,y,op;
for(int i=1;i<=n;i++)
{
op=rd();x=rd();
if(op^2) y=rd();
if(op==1)
{
e[++tot]=(qaz){x,y,i,n};
xmn=min(x,xmn);
ymn=min(y,ymn);
}
else if(op==2) e[x].r=i-1;
else ak[++ta]=(qaz){x,y,i,0};
}
sort(e+1,e+tot+1,cmp);
for(int i=1;i<=tot;i++)
add(1,1,n,e[i].l,e[i].r,i);
build(1,1,n);
for(int i=1;i<=ta;i++) ask(i);
return 0;
}
发表评论