• [JSOI2008]Blue Mary开公司(李超线段树)


    题面:

    大致题意:

    有两种操作:1.给你一条一次函数 2.给你一个x,让你求所有函数中最大的y
    现在有n个操作,要求你对每一个操作2输出最大的y值/100的结果

    Solution:

    这道题可以让我们很好的了解李超线段树
    什么是李超线段树呢?
    李超线段树是用来解决二维直角坐标系上给定直线求最值的一类题目的线段树
    这一类题目往往很难像传统线段树一样下传lazy_tag,所以就需要用到标记永久化
    标记永久化作为预备知识,就不在此多做赘述了
    好了,进入正题


    我们用tag来维护当前区间内mid上的最大y值所属的函数的编号,每次插入新的函数,我们需要分情况讨论来对tag进行更新

    若新插入的函数斜率比tag所记录的函数斜率更大
    1.若新函数在mid上的y值也大于tag,那我们就可以直接把tag覆盖为新的函数,再用原tag去更新左区间。因为原tag对于右区间肯定是不会有贡献了的,但对于左区间却不一定,所以我们需要再次去进行更新,而原区间tag直接被覆盖,右区间则继续用新函数去尝试更新
    图解:此处输入图片的描述
    2.若新函数在mid上的y值小于tag,则再尝试用新函数去更新右区间左区间就不用管了。
    因为新函数在mid上已经比tag要小,则新函数对于左区间肯定不会再有贡献,而对于右区间却可能有贡献,所以还要再往右更新,基本原理跟之前差不多,就不再给出图解

    若新插入的函数斜率比tag所记录的函数斜率更小
    1.若新函数在mid上的值大于tag,我们就覆盖原tag,同时用原tag去更新右区间。由于斜率比原tag要小,则这种情况下,原tag对于左区间肯定是不会有贡献的,但对右区间却不一定,所以还需要去尝试更新右区间
    2.若原区间在mid的值小于tag,则在用新函数去尝试更新左区间,原理和以上大同小异

    Code:

    #include<bits/stdc++.h>
    #define N 1000020
    using namespace std;
    typedef double dl;
    int n,t=1;
    struct sgt_tag{
        #define ls q<<1
        #define rs q<<1|1
        int tot,tag[N*4];
        struct function{dl k,b;}fun[N*4];
        inline dl val(int x,int id){return fun[id].k*(x-1)+fun[id].b;}//因为起始点是1所以x要-1
        inline void ins(dl k,dl b){fun[++tot].k=k;fun[tot].b=b;change(1,1,N,tot);}
        inline void change(int q,int l,int r,int id){
            if(l==r){
                if(val(l,id)>val(l,tag[q]))tag[q]=id;
                return ;
            }
            int mid=(l+r)>>1;
            if(fun[id].k>fun[tag[q]].k){
                if(val(mid,id)>val(mid,tag[q]))change(ls,l,mid,tag[q]),tag[q]=id;
                else change(rs,mid+1,r,id);
            }else{
                if(val(mid,id)>val(mid,tag[q]))change(rs,mid+1,r,tag[q]),tag[q]=id;
                else change(ls,l,mid,id);
            }
        }
        inline dl query(int q,int l,int r,int x){
            if(l==r)return val(l,tag[q]);
            int mid=(l+r)>>1;dl ans=val(x,tag[q]);
            if(x<=mid)ans=max(ans,query(ls,l,mid,x));
            else ans=max(ans,query(rs,mid+1,r,x));
            return ans;
        }
    }T;
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
        return x*f;
    }
    int main(){
        n=read();
        begin:if(t>n)goto end;t++;
        char ch[10];scanf("%s",ch);
        if(ch[0]=='P'){dl k,b;scanf("%lf%lf",&b,&k);T.ins(k,b);}
        if(ch[0]=='Q'){int x=read();dl ans=T.query(1,1,N,x);printf("%d
    ",(int)ans/100);}
        goto begin;
        end:return 0;
    }
    
  • 相关阅读:
    Linux基础命令(一)
    You've made choice
    protege推理
    字符编码
    第二次作业
    数据类型-集合set
    数据类型-元组&字典
    数据类型-列表
    数据类型-数值&字符串
    流程控制之for循环
  • 原文地址:https://www.cnblogs.com/NLDQY/p/10147594.html
Copyright © 2020-2023  润新知