• [bzoj4311] 向量


    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

    Sample Output

    18

    15

    HINT

    (n leq 200000) (1 leq x,y leq 10^6)


    想法

    每个向量只对一定范围内的查询操作可能有贡献,于是可以线段树分治。

    具体就是将询问按时间编号为 (1~m) ,建一棵线段树。
    每个向量 (insert) 到它有贡献的区间中,注意找到完全包含在它的贡献区间的节点后打上标记就行了,不需要下放
    怎么打标记呢?就是对每个节点开一个 (vector) ,把向量扔进去就行了。

    接着我们考虑,“求集合中与((x,y)) 点积的最大值” 怎么搞。
    对于集合中的两个向量,((u1,v1) 和 (u2,v2)) ,不妨设 (u1<u2)
    若前者不如后者,即 ((u1,v1) cdot (x,y) < (u2,v2) cdot (x,y))
    打开并整理一下得出 (-frac{x}{y} < frac{v2-v1}{u2-u1})
    也就是说我们需要维护斜率递减的凸包。

    为了维护方便,我们将向量们按 (x) 坐标从小到大依次 (insert) 到线段树中,线段树的每个节点维护一个凸包。
    最终查询的时候,在线段树上从根到特定叶子的路径上每个节点维护的凸包中二分最优解,更新答案。
    每次查询复杂度 (O(log^2n)) , 总复杂度 (O(nlog^2n))


    代码

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<vector>
     
    using namespace std;
     
    int read(){
        int x=0;
        char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
        return x;
    }
     
    const int N = 200005;
    typedef long long ll;
     
    int tot,n,m;
    struct data{ int x,y,l,r; }d[N],q[N];
    bool cmp(data x,data y) { return x.x<y.x; }
     
    int root,cnt;
    int ch[N*2][2];
    vector<data> v[N*2];
    void build(int x,int l,int r){
        if(l==r) return;
        int mid=(l+r)>>1;
        build(ch[x][0]=++cnt,l,mid);
        build(ch[x][1]=++cnt,mid+1,r);
    }
    bool bigk(data a,data b,data c) { return 1ll*(b.y-a.y)*(c.x-b.x)<=1ll*(c.y-b.y)*(b.x-a.x); }
    void ins(int x,int l,int r,int L,int R,int c){
        if(l==L && r==R){ 
            while(v[x].size()>1 && bigk(v[x][v[x].size()-2],v[x][v[x].size()-1],d[c])) v[x].pop_back();
            v[x].push_back(d[c]);
            return;
        }
        int mid=(l+r)>>1;
        if(R<=mid) ins(ch[x][0],l,mid,L,R,c);
        else if(L>mid) ins(ch[x][1],mid+1,r,L,R,c);
        else{
            ins(ch[x][0],l,mid,L,mid,c);
            ins(ch[x][1],mid+1,r,mid+1,R,c);
        }
    }
    bool check(data a,data b,data c) { return 1ll*c.y*(a.y-b.y)<1ll*c.x*(b.x-a.x); }
    ll ask(int x,int l,int r,int c){
        ll ret=0;
        if(v[x].size()>=1){
            int L=0,R=v[x].size()-1,mid;
            while(L<R){
                mid=(L+R)>>1;
                if(check(v[x][mid],v[x][mid+1],q[c])) L=mid+1;
                else R=mid;
            }
            ret=1ll*q[c].x*v[x][L].x+1ll*q[c].y*v[x][L].y;
        }
         
        if(l==r) return ret;
        int mid=(l+r)>>1;
        if(c<=mid) return max(ret,ask(ch[x][0],l,mid,c));
        return max(ret,ask(ch[x][1],mid+1,r,c));
    }
     
    int main()
    {
        int Q,t,id,x,y;
        Q=read();
        while(Q--){
            t=read();
            if(t==1) {
                x=read(); y=read();
                d[++m]=(data){x,y,n+1,-1};
            }
            else if(t==2){
                id=read();
                d[id].r=n;
            }
            else {
                x=read(); y=read();
                q[++n]=(data){x,y,0,0};
            }
        }
        for(int i=1;i<=m;i++) if(d[i].r==-1) d[i].r=n;
        sort(d+1,d+1+m,cmp);
         
        build(root=++cnt,1,n);
        for(int i=1;i<=m;i++)
            if(d[i].l<=d[i].r) ins(root,1,n,d[i].l,d[i].r,i);
         
        for(int i=1;i<=n;i++) printf("%lld
    ",ask(root,1,n,i));
         
        return 0;
    } 
    
    
    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    多线程
    python 面向对象
    selenium 安装 以及相关环境
    pyquery 库的方法
    Python 面向对象的补充
    python 面向对象
    想造轮子的时候,ctrl+f一下
    C#三层开发做学生管理系统
    C# 我是个传奇的 using
    啦啦啦 啦啦 啦 啦 啦 啦啦 啦 啦 啦
  • 原文地址:https://www.cnblogs.com/lindalee/p/11366747.html
Copyright © 2020-2023  润新知