【BZOJ3533】向量集(SDOI2014)-线段树+凸壳+二分

测试地址:向量集
做法:本题需要用到线段树+凸包+二分。
首先恭喜一下自己达成BZOJ200AC……(突然想起,这不会有多少人看得见)
令询问的向量为(a,b),序列中第i个向量为(xi,yi),那么要求lir时,axi+byi的最大值,令这个式子为k,则有:
yi=abxi+kb
注意到b=0时只需要算xi的最大值就行了,所以我们不考虑ab没有意义的问题。
b>0时,这相当于在所有点(xi,yi)上作斜率为ab的直线,求最大的截距,显然可以维护一个上凸壳,然后在上凸壳上二分解决。
b<0时,直接将所有xi,yi变成它们的相反数,这样就又转化为了求最大截距的问题,用上面的方法做就行了。
然而这只是一次询问的情况,要支持多次区间询问,我们就要用线段树维护一个区间内点的上凸壳。那插入操作怎么办?我们可以一开始先开好线段树的空间,但是只有当一个区间内的点被插入完整时,才计算这个区间的凸壳。显然可知,处理凸壳的总时间是O(nlogn),一次询问的时间是O(log2n),可以通过此题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=400010;
const ll inf=1000000000ll*1000000000ll;
int n,tot=0,pos[N];
char s,op[3];
struct point
{
    ll x,y;
    point operator + (point a) const
    {
        point s={x+a.x,y+a.y};
        return s;
    }
    point operator - (point a) const
    {
        point s={x-a.x,y-a.y};
        return s;
    }
    ll operator * (point a) const
    {
        return x*a.y-y*a.x;
    }
};

ll dot(point a,point b)
{
    return a.x*b.x+a.y*b.y;
}

struct convex
{
    int n;
    point *a;

    void init(int now)
    {
        n=0;
        a=new point [now+5];
    }

    void push(point s)
    {
        while(n>1&&(s-a[n])*(a[n]-a[n-1])<=0) n--;
        a[++n]=s;
    }

    void merge(convex &lc,convex &rc)
    {
        init(lc.n+rc.n);

        int l=1,r=1;
        while(l<=lc.n&&r<=rc.n)
        {
            int lx=lc.a[l].x,rx=rc.a[r].x;
            if (lx<rx||(lx==rx&&lc.a[l].y<rc.a[r].y)) push(lc.a[l]),l++;
            else push(rc.a[r]),r++;
        }
        if (l>lc.n)
        {
            while(r<=rc.n)
                push(rc.a[r]),r++;
        }
        else
        {
            while(l<=lc.n)
                push(lc.a[l]),l++;
        }
    }

    ll find(ll x,ll y)
    {
        point s={x,y};
        if (y<0) s.x=-x,s.y=-y;
        int l=1,r=n;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if (dot(a[mid],s)<=dot(a[mid+1],s)) l=mid+1;
            else r=mid;
        }
        return dot(s,a[l]);
    }
};

struct Seg
{
    convex a[2];
}seg[N<<2];

ll decode(ll x,ll lastans)
{
    return x^(lastans&0x7fffffff);
}

void buildtree(int no,int l,int r)
{
    if (l==r) {pos[l]=no;return;}
    int mid=(l+r)>>1;
    buildtree(no<<1,l,mid);
    buildtree(no<<1|1,mid+1,r);
}

void add(int id,ll x,ll y)
{
    int now=pos[id];
    point nxt={x,y};
    seg[now].a[0].init(1);seg[now].a[0].push(nxt);
    nxt.x=-x,nxt.y=-y;
    seg[now].a[1].init(1);seg[now].a[1].push(nxt);
    while((now&1)&&now>1)
    {
        now>>=1;
        seg[now].a[0].merge(seg[now<<1].a[0],seg[now<<1|1].a[0]);
        seg[now].a[1].merge(seg[now<<1].a[1],seg[now<<1|1].a[1]);
    }
}

ll query(int no,int l,int r,int s,int t,ll x,ll y)
{
    if (l>=s&&r<=t) return seg[no].a[y<0].find(x,y);
    ll ans=-inf;
    int mid=(l+r)>>1;
    if (s<=mid) ans=max(ans,query(no<<1,l,mid,s,t,x,y));
    if (t>mid) ans=max(ans,query(no<<1|1,mid+1,r,s,t,x,y));
    return ans;
}

int main()
{
    scanf("%d %c",&n,&s);

    ll lastans=0;
    buildtree(1,1,n);
    for(int i=1;i<=n;i++)
    {
        ll x,y;
        int l,r;
        scanf("%s",op);
        if (op[0]=='A')
        {
            scanf("%lld%lld",&x,&y);
            if (s!='E') x=decode(x,lastans),y=decode(y,lastans);
            add(++tot,x,y);
        }
        else
        {
            scanf("%lld%lld%d%d",&x,&y,&l,&r);
            if (s!='E')
            {
                x=decode(x,lastans),y=decode(y,lastans);
                l=decode(l,lastans),r=decode(r,lastans);
            }
            printf("%lld\n",lastans=query(1,1,n,l,r,x,y));
        }
    }

    return 0;
}
posted @ 2018-05-20 12:28  Maxwei_wzj  阅读(125)  评论(0编辑  收藏  举报