bzoj 3533: [Sdoi2014]向量集 线段树+凸包+二分

来源:互联网 发布:电子数据交换的意思 编辑:程序博客网 时间:2024/05/23 15:39

题意

维护一个向量集合,在线支持以下操作:
“A x y (|x|,|y| < =10^8)”:加入向量(x,y);
” Q x y l r (|x|,|y| < =10^8,1 < =L < =R < =T,其中T为已经加入的向量个数)询问第L个到第R个加入的向量与向量(x,y)的点积的最大值。
集合初始时为空。强制在线。
1 < =N < =4×10^5

分析

世界上最开心的事情莫过于一遍AC计算几何+数据结构题~~

答案是要求z=xxi+yyi的最大值,把式子画一下就变成了yi=xxiy+zy,也就是说,现在给出了一条斜率为xy的直线,要你去切给出的若干个点,若y<0则要求截距最小,反之则斜率最大。
现在只讨论y<0的情况。
显然最优解必然在点集的下凸壳上。那么我们可以考虑把下凸壳求出来,然后在上面二分即可。
网上很多题解都是用三分,的确这是个单峰函数,但考虑到是定义在正整数集上的单峰函数,所以实际上是可以用二分来求的。
现在要求的是一个区间的下凸壳,考虑用线段树来维护。
但求下凸壳的复杂度是O(nlogn)的,我们不能每加入一个点就把其经过的所有区间都更新。
这里有一个很机智的办法,就是注意到若该区间包含一个还没被加入的位置,则一定不会被询问到。所以我们每加入一个点,就暴力把那些右端点为该点的区间跑一遍下凸壳即可。
y>0则是上凸壳。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<vector>using namespace std;typedef long long LL;const int N=400005;const LL inf=100000000000000000;int n,tot,que[N];pair<int,int> tmp[N],a[N];struct tree{vector<pair<int,int> > u,d;}t[N*5];LL ans;int read(){    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;}inline int decode(int x,LL ans){    return x^(ans&0x7fffffff);}double get_k(pair<int,int> x,pair<int,int> y){    return (double)(x.second-y.second)/(x.first-y.first);}void build(int d,int l,int r){    int tot=0;    for (int i=l;i<=r;i++) tmp[++tot]=a[i];    sort(tmp+1,tmp+tot+1);    for (int i=tot;i>1;i--) if (tmp[i].first==tmp[i-1].first) tmp[i-1].second=tmp[i].second;    tot=unique(tmp+1,tmp+tot+1)-tmp-1;    int head=1,tail=1;que[1]=1;    for (int i=2;i<=tot;i++)    {        while (head<tail&&get_k(tmp[que[tail-1]],tmp[que[tail]])<get_k(tmp[que[tail]],tmp[i])) tail--;        que[++tail]=i;    }    while (head<=tail) t[d].u.push_back(tmp[que[head]]),head++;    tot=0;    for (int i=l;i<=r;i++) tmp[++tot]=a[i];    sort(tmp+1,tmp+tot+1);    for (int i=1;i<tot;i++) if (tmp[i].first==tmp[i+1].first) tmp[i+1].second=tmp[i].second;    tot=unique(tmp+1,tmp+tot+1)-tmp-1;    head=1;tail=1;que[1]=1;    for (int i=2;i<=tot;i++)    {        while (head<tail&&get_k(tmp[que[tail-1]],tmp[que[tail]])>get_k(tmp[que[tail]],tmp[i])) tail--;        que[++tail]=i;    }    while (head<=tail) t[d].d.push_back(tmp[que[head]]),head++;}LL get_ans(pair<int,int> x,pair<int,int> y){    return (LL)x.first*y.first+(LL)x.second*y.second;}LL query(int d,pair<int,int> p){    int x=p.first,y=p.second;    if (!y) return max(x*t[d].d[0].first,x*t[d].d[t[d].d.size()-1].first);    if (y<0)    {        int l=0,r=t[d].d.size()-2;        while (l<=r)        {            int mid=(l+r)/2;            if (get_ans(t[d].d[mid],p)>get_ans(t[d].d[mid+1],p)) r=mid-1;            else l=mid+1;        }        return get_ans(t[d].d[r+1],p);    }    else    {        int l=0,r=t[d].u.size()-2;        while (l<=r)        {            int mid=(l+r)/2;            if (get_ans(t[d].u[mid],p)>get_ans(t[d].u[mid+1],p)) r=mid-1;            else l=mid+1;        }        return get_ans(t[d].u[r+1],p);    }}void ins(int d,int l,int r,int x){    if (r==x) build(d,l,r);    if (l==r) return;    int mid=(l+r)/2;    if (x<=mid) ins(d*2,l,mid,x);    else ins(d*2+1,mid+1,r,x);}LL query(int d,int l,int r,int x,int y,pair<int,int> p){    if (x>y) return -inf;    if (l==x&&r==y) return query(d,p);    int mid=(l+r)/2;    return max(query(d*2,l,mid,x,min(y,mid),p),query(d*2+1,mid+1,r,max(x,mid+1),y,p));}int main(){    n=read();    char ty[2];scanf("%s",ty);    for (int i=1;i<=n;i++)    {        char op[2];        scanf("%s",op);        if (op[0]=='A')        {            int x=read(),y=read();            if (ty[0]!='E') x=decode(x,ans),y=decode(y,ans);            a[++tot]=make_pair(x,y);            ins(1,1,n,tot);        }        else        {            int x=read(),y=read(),l=read(),r=read();            if (ty[0]!='E') x=decode(x,ans),y=decode(y,ans),l=decode(l,ans),r=decode(r,ans);            printf("%lld\n",ans=query(1,1,n,l,r,make_pair(x,y)));        }    }    return 0;}
原创粉丝点击