zoukankan      html  css  js  c++  java
  • 【bzoj4311】向量 线段树对时间分治+STL-vector维护凸包

    题目描述

    你要维护一个向量集合,支持以下操作:
    1.插入一个向量(x,y)
    2.删除插入的第i个向量
    3.查询当前集合与(x,y)点积的最大值是多少。如果当前是空集输出0

    输入

    第一行输入一个整数n,表示操作个数
    接下来n行,每行先是一个整数t表示类型,如果t=1,输入向量
    (x,y);如果t=2,输入id表示删除第id个向量;否则输入(x,y),查询
    与向量(x,y)点积最大值是多少。
    保证一个向量只会被删除一次,不会删没有插入过的向量

    输出

    对于每条t=3的询问,输出一个答案

    样例输入

    5
    1 3 3
    1 1 4
    3 3 3
    2 1
    3 3 3

    样例输出

    18
    15


    题解

    线段树对时间分治+STL-vector维护凸包

    首先需要知道向量 $(x_1,y_1)$ 和 $(x_2,y_2)$ 的点积(数量积)为 $x_1x_2+y_1y_2$ 。

    问题转化为给出一堆 $x$ 、$y$ 和 $a$ 、$b$ ,求 $ax+by$ 的最大值。

    设 $ax+by=c$ ,则整理可得 $y=-frac abx+frac cb$ ,要让 $c$ 最大即让截距最大。因此答案一定在上凸壳上取到。

    由于有删除操作,因此使用线段树对时间分治,把一个向量出现的时间段分为线段树上的log个。

    然后考虑怎么统计答案:由于平衡树维护凸包的时间复杂度时均摊的,因此不能在线段树上进行插入与恢复的操作。考虑到答案所在的每一段互不影响,因此可以对于线段树的每个节点维护凸包,查询时在每一段上进行二分,取最大值即为答案。

    因此使用STL-vector,对于线段树的每个节点,维护上凸壳;查询时在其到线段树根节点的每个节点的凸包上二分,每个节点求出最优解后再取最大值即为答案。

    这样做的时间复杂度时 $O(nlog ^2n)$ 。

    存在一种更优的解法:对每个询问按照 $-frac ab$ 从大到小排序,这样决策就是按 $x$ 单调不降的了。每个节点维护当前决策位置,统计时不断判断下一个是否比当前的优即可。

    时间复杂度 $O(nlog n)$ ,然而跑不过 $O(nlog^2n)$ 什么鬼。。。

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    #define N 200010
    #define lson l , mid , x << 1
    #define rson mid + 1 , r , x << 1 | 1
    using namespace std;
    typedef long long ll;
    struct point
    {
        ll x , y;
        point() {}
        point(ll a , ll b) {x = a , y = b;}
        point operator-(const point &a)const {return point(x - a.x , y - a.y);}
        ll operator*(const point &a)const {return x * a.y - y * a.x;}
    };
    struct data
    {
        point p;
        int l , r;
    }a[N] , b[N] , c[N];
    vector<point> v[N << 2];
    int pos[N << 2] , val[N] , ta , tb , tc;
    ll ans[N];
    bool cmp1(const data &a , const data &b)
    {
        return a.p.x == b.p.x ? a.p.y < b.p.y : a.p.x < b.p.x;
    }
    bool cmp2(const data &a , const data &b)
    {
        return a.p * b.p < 0;
    }
    inline ll calc(point a , point b)
    {
        return a.x * b.x + a.y * b.y;
    }
    void insert(int b , int e , point p , int l , int r , int x)
    {
        if(b <= l && r <= e)
        {
            while(v[x].size() > 1 && (p - v[x][v[x].size() - 1]) * (p - v[x][v[x].size() - 2]) <= 0) v[x].pop_back();
            v[x].push_back(p);
            return;
        }
        int mid = (l + r) >> 1;
        if(b <= mid) insert(b , e , p , lson);
        if(e > mid) insert(b , e , p , rson);
    }
    ll query(int k , point p , int l , int r , int x)
    {
        ll ans = 0;
        if(v[x].size())
        {
            while(pos[x] < (int)v[x].size() - 1 && calc(p , v[x][pos[x] + 1]) >= calc(p , v[x][pos[x]])) pos[x] ++ ;
            ans = calc(p , v[x][pos[x]]);
        }
        if(l == r) return ans;
        int mid = (l + r) >> 1;
        if(k <= mid) return max(ans , query(k , p , lson));
        else return max(ans , query(k , p , rson));
    }
    int main()
    {
        int n , i , opt , k;
        scanf("%d" , &n);
        for(i = 1 ; i <= n ; i ++ )
        {
            scanf("%d" , &opt);
            if(opt == 1) scanf("%lld%lld" , &a[i].p.x , &a[i].p.y) , a[i].l = i , a[i].r = n , val[++ta] = i;
            else if(opt == 2) scanf("%d" , &k) , a[val[k]].r = i - 1 , a[i].r = -1;
            else tc ++ , scanf("%lld%lld" , &c[tc].p.x , &c[tc].p.y) , c[tc].l = i;
        }
        for(i = 1 ; i <= n ; i ++ )
            if(a[i].l)
                b[++tb] = a[i];
        sort(b + 1 , b + tb + 1 , cmp1);
        for(i = 1 ; i <= tb ; i ++ ) insert(b[i].l , b[i].r , b[i].p , 1 , n , 1);
        sort(c + 1 , c + tc + 1 , cmp2);
        for(i = 1 ; i <= tc ; i ++ ) ans[c[i].l] = query(c[i].l , c[i].p , 1 , n , 1);
        for(i = 1 ; i <= n ; i ++ )
            if(!a[i].r)
                printf("%lld
    " , ans[i]);
        return 0;
    }
    

     

  • 相关阅读:
    CSDN专栏收集
    Android英文文档翻译系列(5)——VPNService
    Android英文文档翻译系列(4)——PopupWindow
    Android英文文档翻译系列(3)——AsyncTask
    Android英文文档翻译系列(2)——HandlerThread
    Android英文文档翻译系列(1)——AlarmManager
    Apktool源码解析——第二篇
    Apktool源码解析——第一篇
    AndroidのBuild工具之Ant动手实践
    Java的switch是否支持String作为参数,还支持哪些类型?
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8110074.html
Copyright © 2011-2022 走看看