李超线段树

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

代码还没写,占坑待填

posted @ 2019-04-29 21:43  OIerC  阅读(249)  评论(0编辑  收藏  举报