zoukankan      html  css  js  c++  java
  • P3309-[SDOI2014]向量集【线段树,凸壳】

    正题

    题目链接:https://www.luogu.com.cn/problem/P3309


    题目大意

    (n)个操作

    1. 在序列末尾加入一个向量((x,y))
    2. 询问加入的第(lsim r)个向量中的一个向量和((x,y))的点积最大值

    强制在线,点积的定义为(x_1x_2+y_1y_2)


    解题思路

    如果对于一个((x,y))对于两个((x_1,y_1))((x_2,y_2))如果后者更大那么有

    [x_2x+y_2y>x_1x+y_1yRightarrow frac{y}{x}leq frac{x_2-x_1}{y_2-y_1} ]

    好像和斜率有关,可以维护凸壳来做,因为(y)可能是负数,如果是负数的时候就要求的是下凸壳了,所以两个凸壳都要维护。

    因为强制在线所以上不了传统艺能( ext{CDQ})

    那怎么动态维护区间凸壳,平衡树支持动态插入但不支持区间问题。所以考虑线段树,因为一个位置修改了之后就不会再修改,而且是从左往右加的,可以利用这个性质。

    每次我们修改一个位置后,如果一个区间([L,R])的节点内已经插入了(R-L+1)个向量(也就是都插完了)的话就直接把它的两个儿子的凸壳合并起来。

    然后询问的时候分成(log n)个区间询问的答案取最大值就好了。

    合并凸壳的是用归并排序的话时间复杂度(O(nlog n))


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #define ll long long
    using namespace std;
    struct point{
        ll x,y;
        point(ll xx=0,ll yy=0)
        {x=xx;y=yy;return;}
    }z;
    point operator+(point x,point y)
    {return point(x.x+y.x,x.y+y.y);}
    point operator-(point x,point y)
    {return point(x.x-y.x,x.y-y.y);}
    ll operator^(point x,point y)
    {return x.x*y.y-x.y*y.x;}
    ll operator*(point x,point y)
    {return x.x*y.x+x.y*y.y;}
    bool operator<(point x,point y)
    {return (x.x==y.x)?x.y<y.y:x.x<y.x;}
    
    const ll N=4e5+10;
    char pe[3];
    ll n,num,siz[N<<2];
    vector<point> v[N<<2][2],tmp;
    
    void Make(ll x){
        ll ls=x*2,rs=x*2+1;
        for(ll k=0;k<2;k++){
            ll i=0,j=0,l1=v[ls][k].size()-1,l2=v[rs][k].size()-1;
            tmp.clear();
            while(i<=l1||j<=l2){
                if(i>l1||(j<=l2&&v[rs][k][j]<v[ls][k][i]))
                    tmp.push_back(v[rs][k][j]),j++;
                else tmp.push_back(v[ls][k][i]),i++;
            }
            ll cnt=0;
            for(ll i=0;i<tmp.size();i++){
                while(cnt>1&&((v[x][k][cnt-1]-v[x][k][cnt-2])^(tmp[i]-v[x][k][cnt-1]))>=0)
                    v[x][k].pop_back(),cnt--;
                v[x][k].push_back(tmp[i]);cnt++;
            }
        }
        return;
    }
    ll Calc(ll x,point p){
        ll f=0;
        if(p.y<0)p=z-p,f^=1;
        ll l=0,r=v[x][f].size()-2;
        while(l<=r){
            ll mid=(l+r)>>1;
            point tmp=v[x][f][mid+1]-v[x][f][mid];tmp.x*=-1;
            if(p.x*tmp.x>=p.y*tmp.y)r=mid-1;
            else l=mid+1;
        }
        return p*v[x][f][l];
    }
    void Change(ll x,ll L,ll R,ll pos,point p){
        if(L==R){
            v[x][0].push_back(p);
            v[x][1].push_back(z-p);
            return;
        }
        ll mid=(L+R)>>1;siz[x]++;
        if(pos<=mid)Change(x*2,L,mid,pos,p);
        else Change(x*2+1,mid+1,R,pos,p);
        if(siz[x]==R-L+1)Make(x);
    }
    ll Ask(ll x,ll L,ll R,ll l,ll r,point p){
        if(L==l&&R==r)return Calc(x,p);
        ll mid=(L+R)>>1;
        if(r<=mid)return Ask(x*2,L,mid,l,r,p);
        if(l>mid)return Ask(x*2+1,mid+1,R,l,r,p);
        return max(Ask(x*2,L,mid,l,mid,p),Ask(x*2+1,mid+1,R,mid+1,r,p));
    }
    void dc(ll &x,ll lastans) {
        if(pe[0]=='E')return;
        x=x^(lastans&0x7fffffff);
        return;
    }
    signed main()
    {
        scanf("%lld%s",&n,pe);
        ll last=0;
        for(ll i=1;i<=n;i++){
            char op[3];ll x,y,l,r;
            scanf("%s%lld%lld",op,&x,&y);
            dc(x,last);dc(y,last);
            if(op[0]=='A'){
                ++num;
                Change(1,1,n,num,point(x,y));
            }
            else{
                scanf("%lld%lld",&l,&r);dc(l,last);dc(r,last);
                printf("%lld
    ",last=Ask(1,1,n,l,r,point(x,y)));
            }
        }
        return 0;
    }
    
  • 相关阅读:
    javaee_正则表达式基础和常用表达式
    hello2源代码分析
    servlet_filterj简介
    hello1的web.xml解析
    Annotation
    注入(Injection)
    容器(Container)
    Building Tool(Maven/Gradle)
    JavaWeb的历史与发展趋势
    Build Tools
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14298414.html
Copyright © 2011-2022 走看看