知识点 - 李超树

知识点 - 李超树

解决问题类型:

李超树是一种线段树的应用,主要应用场合为:单点集合域,区间等差插入,求单点最值。

  • 动态地插入多条二维线段,询问某个 x x x对应的 y y y值的最值。
  • 修改 [ L , R , b , k ] [L,R,b,k] [L,R,b,k],对于第 i ∈ [ L , R ] i∈[L,R] i[L,R]个集合,插入 b + ( i − L ) k b+(i−L)k b+(iL)k,询问某个集合的最大值。

实现

  1. 每个线段树结点记录的是 x = m i d x = mid x=mid 时的最值线段

  2. 先考虑怎么查询。因为只能记录 x = m i d x = mid x=mid 时的最值线段,所有包含pos的大区间的答案可能不是最优解。所以要递归的往下查找,直到 L = R L=R L=R

  3. 考虑插入一条线段。若区间无线段,则成为最优线段。否则进行比较。

  4. O l d Old Old为原线段在 l l l上的值, O l d Old Old_为在 r r r上的值。 N e w New New 同理。

  5. 简单的情况, O l d > N e w Old >New Old>New, O l d _ > N e w _ Old\_ > New\_ Old_>New_或相反时,显然可以直接判断孰优孰劣。

  6. 考虑存在交点的情况。

在这里插å¥å›¾ç‰‡æè¿°

  • 右图的情况下,交点小于一半,根据两条线的位置关系可以判断,右区间还是原来的线段更优。所以这种情况下,只需要递归的做左边即可。
  • 考虑左图的情况,此时当前区间的最值在 n e w new new上,再结合我们的查询,我们还要记录 o l d old old(再右半边存在 o l d old old优于 n e w new new的情况)。所以将 o l d old old在右区间往下传递。
  • 位置相反的时候也这样考虑一下就行。

复杂度

  • 线段定义域固定时,查询和插入的时间复杂度都是 O ( l o g n ) O(logn) O(logn)
  • 线段定义域不固定时,因为修改区间可能分为 l o g log log个区间,每个区间可能递归到底,所以插入的时间复杂度为 O ( l o g 2 n ) O(log^2n) O(log2n)查询复杂度为 O ( l o g n ) O(logn) O(logn)
  • 查询为 O ( l o g n ) O(logn) O(logn)可以直接使用不固定的代码,时间复杂度该多少就是多少 ,但是它长啊 。

例题

BZOJ 1568

代码

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
#define debug(str) cerr<<#str<<" = "<<str<<'\n';
const LL mod=1e9+7;
const int maxn=5e4+9;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
/*_________________________________________________________head*/

struct line{
    bool have;
    double k,b;
}tr[maxn<<2];
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid (l+r>>1)
void update(double k,double b,int rt=1,int l=1,int r=5e4){ // insert y=kx+b
    if(!tr[rt].have){
        tr[rt].k=k,
        tr[rt].b=b,
        tr[rt].have=1;
        return;
    }
    double Old=tr[rt].k*l+tr[rt].b,Old_=tr[rt].k*r+tr[rt].b;
    double New=k*l+b,New_=k*r+b;
    if(Old>=New&&Old_>=New_)return;
    if(Old<=New&&Old_<=New_){
        tr[rt].k=k,
        tr[rt].b=b;
        return;
    }
    // k won't equiv tr[rt].k
    double pos=-(b-tr[rt].b)/(k-tr[rt].k);
    if(Old<=New){
        if(pos<=mid){
            update(k,b,ls,l,mid);
        }
        else{
            update(tr[rt].k,tr[rt].b,rs,mid+1,r);
            tr[rt].k=k,tr[rt].b=b;
        }
    }
    else{
        if(pos>=mid){
            update(k,b,rs,mid+1,r);
        }
        else{
            update(tr[rt].k,tr[rt].b,ls,l,mid);
            tr[rt].k=k,tr[rt].b=b;
        }
    }
}

double query(int x,int rt=1,int l=1,int r=5e4){ // query most value at x
    double ans=-2e18;
    if(tr[rt].have)
        ans=tr[rt].k*x+tr[rt].b;
    if(l==r)return ans;
    if(x<=mid)ans=max(ans,query(x,ls,l,mid));
    else ans=max(ans,query(x,rs,mid+1,r));
    return ans;
}

char str[50];
int main(){
    int t=rd();
    while(t--){
        scanf("%s",str);
        if(str[0]=='P'){
            double k,b;
            scanf("%lf%lf",&b,&k);
            update(k,b-k);
        }
        else{
            int x;scanf("%d",&x);
            printf("%d\n",max(0,(int)floor(query(x)/100.0)));
        }

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值