bzoj3533 凸包+线段树+三分

1 篇文章 0 订阅

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3533

大意:维护一个向量集合,在线支持以下操作: “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)的点积的最大值。
集合初始时为空。

答案肯定在凸包上(并不会证明,不过画下图就很显然了)且y>0在上凸壳。用线段树维护插入顺序,不过如果对于每次插入都维护一次凸包就boom!boom!boom!所以我们可以一个区间所有插入完成后再维护凸包。因为答案单峰就用三分查找答案。

把vector当参数传当过程中的时候最好要用下引用(const ….)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#include<ctime>
#include<algorithm>
using namespace std;
#define M 400030
#define INF 144115188075855872ll
typedef long long ll;
#define Decode(x) ((x)^(ans & 0x7fffffff))
struct pt {
    ll x,y;
    inline pt operator -(const pt &a) const {
        return (pt){x-a.x,y-a.y};
    }
    inline ll operator *(const pt &a)  const {
        return x*a.x+y*a.y;
    }
};
typedef vector<pt> vt;
struct tree {
    vt v[2];
    int l,r;
}T[M*4];
int tot,n,root;
ll ans;
char Ty[10],s[10];
inline int read()
{
    char ch;    int k=1;
    while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') k=-1;
    int r=ch-'0';
    while (ch=getchar(),ch>='0'&&ch<='9') r=r*10-'0'+ch;
    return r*k;
}
inline ll cross(pt a,pt b)
{ return a.x*b.y-a.y*b.x; }

inline void Merge1(const vt &l,const vt &r,vt &k)
{
    int S1=l.size(),S2=r.size(),i=0,j=0;
    int top=0;
    pt p;
    while (i<S1||j<S2) {
        if (j==S2) p=l[i++]; 
        else if (i==S1) p=r[j++];
        else if (l[i].x<r[j].x||(l[i].x==r[j].x&&l[i].y<r[j].y)) p=l[i++];
        else p=r[j++];
        while (top>=2&&cross(k[top-1]-k[top-2],p-k[top-1])>=0) k.pop_back(),--top;
        k.push_back(p); ++top;
    }
}
inline void Merge2(const vt &l,const vt &r,vt &k)
{
    int S1=l.size(),S2=r.size(),i=0,j=0;
    int top=0;
    pt p;
    while (i<S1||j<S2) {
        if (j==S2) p=l[i++]; 
        else if (i==S1) p=r[j++];
        else if (l[i].x<r[j].x||(l[i].x==r[j].x&&l[i].y<r[j].y)) p=l[i++];
        else p=r[j++];
        while (top>=2&&cross(k[top-1]-k[top-2],p-k[top-1])<=0) k.pop_back(),--top;
        k.push_back(p); ++top;
    }
}
inline void Ins(int &k,int l,int r,const int &x,const pt &p)
{
    if (!k) k=++tot;
    if (l==r) {
        T[k].v[0].push_back(p);
        T[k].v[1].push_back(p);
        return;
    }
    int mid=l+r>>1;
    if (x<=mid) Ins(T[k].l,l,mid,x,p);
    else Ins(T[k].r,mid+1,r,x,p);
    if (r==x) {
        Merge1(T[T[k].l].v[0],T[T[k].r].v[0],T[k].v[0]);
        Merge2(T[T[k].l].v[1],T[T[k].r].v[1],T[k].v[1]);
    }
}
inline ll Sf(const vt &a,const pt &p)
{
    int l=0,r=a.size()-1,lmid,rmid;
    while (r-l>2) {
        lmid=(l+l+r)/3,rmid=(l+r+r)/3;
        if ((a[lmid]*p)>(a[rmid]*p)) r=rmid;
        else l=lmid;
    }
    ll tmp=-INF;
    //cerr<<a[r]*p<<endl;
    for (int i=l;i<=r;++i)
        tmp=max(tmp,a[i]*p);
    return tmp;
}
inline ll Query(int k,int l,int r,const int &x,const int &y,const pt &p)
{
    if (!k) return -INF;
    if (l==x&&r==y) return (p.y>0) ? Sf(T[k].v[0],p) : Sf(T[k].v[1],p);
    int mid=l+r>>1;
    if (y<=mid) return Query(T[k].l,l,mid,x,y,p);
    if (x>mid) return Query(T[k].r,mid+1,r,x,y,p);
    return max(Query(T[k].l,l,mid,x,mid,p),Query(T[k].r,mid+1,r,mid+1,y,p));
}
int main()
{
    scanf("%d%s",&n,&Ty);
    ans=0;  pt p;   int l,r,num=0;
    for (int i=1;i<=n;++i) {
        scanf("%s",s);
        if (s[0]=='A') {
            p.x=read(); p.y=read();
            if (Ty[0]!='E') p.x=Decode(p.x),p.y=Decode(p.y);
            Ins(root,1,n,++num,p);
        } else {
            p.x=read(); p.y=read(); l=read(); r=read();
            if (Ty[0]!='E') {
                p.x=Decode(p.x),p.y=Decode(p.y);
                l=Decode(l),r=Decode(r);
            }
            ans=Query(root,1,n,l,r,p);
            #ifdef ONLINE_JUDGE
                printf("%lld\n",ans);
            #else 
                printf("%I64d\n",ans);
            #endif
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值