zoukankan      html  css  js  c++  java
  • 李超线段树

    李超线段树是什么?在平面直角坐标系中,它支持插入一条线段(直线),询问(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))

    代码还没写,占坑待填

  • 相关阅读:
    一行代码搞定Dubbo接口调用
    测试周期内测试进度报告规范
    jq 一个强悍的json格式化查看工具
    浅析Docker容器的应用场景
    HDU 4432 Sum of divisors (水题,进制转换)
    HDU 4431 Mahjong (DFS,暴力枚举,剪枝)
    CodeForces 589B Layer Cake (暴力)
    CodeForces 589J Cleaner Robot (DFS,或BFS)
    CodeForces 589I Lottery (暴力,水题)
    CodeForces 589D Boulevard (数学,相遇)
  • 原文地址:https://www.cnblogs.com/ACMSN/p/10793088.html
Copyright © 2011-2022 走看看