• 李超线段树


    李超线段树是什么?在平面直角坐标系中,它支持插入一条线段(直线),询问(x=x_0)时与它相交的线段中(y)的最大(小)值。

    它是如何维护的?抽象的说,就是标记永久化线段树维护区间内从(y=infty)往下看没有被覆盖的长度最大的直线

    标记永久化是什么?就是不用(pushdown)啦,不懂可以看我的博客

    如何维护这个东西?考虑处理一个区间

    • 如果该区间无线段,记录当前线段,返回

    • 如果该区间线段两端都大于当前线段,直接返回

    • 如果该区间线段两端都小于当前线段,修改该区间线段为当前线段并返回

    • 如果线段有交点,判断那根线段在上方的区间长,修改长的一段的值(不变则不修改),递归处理短的一段。

    这个东西复杂度显然是(O(logn))的,因为每次区间长度减半。

    上面那个是插入操作,查询就参照线段树单点查询就行了。

    模板题​

    注意插入的是直线,直接在(1-n)中全部插入即可,复杂度(O(nlogn))

    #include<cstdio>
    #include<algorithm>
    #define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
    #define per(i, a, b) for (register int i=(a); i>=(b); --i)
    using namespace std;
    const int N=50005<<2, n=50000;
    int vis[N]; double k[N], b[N]; char s[N];
    
    inline int read()
    {
     	int x=0,f=1;char ch=getchar();
        for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
        for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
        return x*f;
    }
    
    #define mid (l+r>>1)
    #define ls (rt<<1)
    #define rs (rt<<1|1)
    
    void modify(int rt, int l, int r, double K, double B)
    {
        if (!vis[rt]) {vis[rt]=1; k[rt]=K; b[rt]=B; return;}
        double l1=l*K+B, r1=r*K+B;
        double l2=l*k[rt]+b[rt], r2=r*k[rt]+b[rt];
        if (l1<=l2 && r1<=r2) return;
        if (l1>l2 && r1>r2) {k[rt]=K; b[rt]=B; return;}
        double x=(B-b[rt])/(k[rt]-K);
        if (l1>l2)
        {
            if (x<=mid) modify(ls, l, mid, K, B);
            else modify(rs, mid+1, r, k[rt], b[rt]), k[rt]=K, b[rt]=B;
        }
        else
        {
            if (x>mid) modify(rs, mid+1, r, K, B);
            else modify(ls, l, mid, k[rt], b[rt]), k[rt]=K, b[rt]=B;
        }
    }
    
    double query(int rt, int l, int r, int x)
    {
        if (l==r) return k[rt]*x+b[rt];
        double res=k[rt]*x+b[rt];
        if (x<=mid) res=max(res, query(ls, l, mid, x));
            else res=max(res, query(rs, mid+1, r, x));
        return res;
    }
    
    #undef mid
    #undef ls
    #undef rs
    
    int main()
    {
        int Q=read();
        while (Q--)
        {
            scanf("%s", s);
            if (s[0]=='P')
            {
                double K, B; scanf("%lf%lf", &B, &K);
                modify(1, 1, n, K, B-K);
            }
            else
            {
                int x=read(); double ans=query(1, 1, n, x);
                printf("%lld
    ", (long long)(ans/100));
            }
        }
        return 0;
    }
    

    另一道模板题

    只是把上面的直线变成了线段,需要(O(nlog^2n))的复杂度。

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
    #define per(i, a, b) for (register int i=(a); i>=(b); --i)
    using namespace std;
    const int N=100005<<2, n=100000;
    int vis[N], id[N]; double k[N], b[N], K[N], B[N];
    
    inline int read()
    {
     	int x=0,f=1;char ch=getchar();
        for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
        for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
        return x*f;
    }
    
    #define mid (l+r>>1)
    #define ls (rt<<1)
    #define rs (rt<<1|1)
    
    void upd(int rt, double K, double B, int ID){k[rt]=K; b[rt]=B; id[rt]=ID;}
    
    void modify(int rt, int l, int r, double K, double B, int ID)
    {
        if (!vis[rt]) {vis[rt]=1; upd(rt, K, B, ID); return;}
        double l1=k[rt]*l+b[rt], r1=k[rt]*r+b[rt];
        double l2=K*l+B, r2=K*r+B;
        if (l1>=l2 && r1>=r2) return;
        if (l1<l2 && r1<r2) {upd(rt, K, B, ID); return;}
        double x=1.0*(B-b[rt])/(k[rt]-K);
        if (x<=mid)
        {
            if (l1<=l2) modify(ls, l, mid, K, B, ID);
            else modify(ls, l, mid, k[rt], b[rt], id[rt]), upd(rt, K, B, ID);
        }
        else
        {
            if (l1>l2) modify(rs, mid+1, r, K, B, ID);
            else modify(rs, mid+1, r, k[rt], b[rt], id[rt]), upd(rt, K, B, ID);
        }
    }
    
    void Modify(int rt, int l, int r, int L, int R, int ID, double K, double B)
    {
        if (l>=L && r<=R) {modify(rt, l, r, K, B, ID); return;}
        if (L<=mid) Modify(ls, l, mid, L, R, ID, K, B);
        if (R>mid) Modify(rs, mid+1, r, L, R, ID, K, B);
    }
    
    void Max(int &x, int y, int v)
    {
        double X=K[x]*v+B[x], Y=K[y]*v+B[y];
        if (X<Y || (fabs(X-Y)<1e-7 && x>y)) x=y;
    }
    
    int query(int rt, int l, int r, int x)
    {
        if (l==r) return id[rt];
        int res=id[rt];
        if (x<=mid) Max(res, query(ls, l, mid, x), x);
            else Max(res, query(rs, mid+1, r, x), x);
        return res;
    }
    
    #undef mid
    #undef ls
    #undef rs
    
    int main()
    {
        int Q=read(), ans=0, tot=0;
        while (Q--)
        {
            int opt=read();
            if (!opt)
            {
                int x=(read()+ans-1)%39989+1;
                printf("%d
    ", ans=query(1, 1, n, x));
            }
            else
            {
                int x0=(read()+ans-1)%39989+1, y0=(read()+ans-1)%1000000000+1;
                int x1=(read()+ans-1)%39989+1, y1=(read()+ans-1)%1000000000+1;
                if(x0>x1) swap(x0, x1), swap(y0, y1);
                K[++tot]=1.0*(y0-y1)/(x0-x1); B[tot]=y0-x0*K[tot];
                Modify(1, 1, n, x0, x1, tot, K[tot], B[tot]);
            }
        }
        return 0;
    }
    

    码量稍大的模板题

    其实就是树剖(+)李超线段树,复杂度(O(nlog^3n))

    代码还没写,占坑待填

  • 相关阅读:
    Spring中的AOP
    P2782 友好城市
    1576 最长严格上升子序列
    1058 合唱队形 2004年NOIP全国联赛提高组
    5294 挖地雷
    1643 线段覆盖 3
    4768 跳石头
    1026 逃跑的拉尔夫
    2727:仙岛求药
    codevs 4888 零件分组
  • 原文地址:https://www.cnblogs.com/ACMSN/p/10793088.html
Copyright © 2020-2023  润新知