李超树学习笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Roll_Keyboard/article/details/81127266

        牛客多校出了这样一道题,一颗有边权的树,对于一条路径(u,v),假设经过的边权为e1,e2,e3,...ef,则该路径权值cal(u,v)为(e2e1)2+(e3e2)2+...+efef1)2,对每个点求fu=maxucal(u,v)
        我一开始的想法是点分治,每次dfs出重心root到某个点u的cal(root,u)以及最上面的边权v,然后不断更新,路径(u,v)的价值就是cal(u,root)+val(v,root)+(v1v2)2,然而最后发现没法维护出最大值,因为要维护的是两个值。看了看题解,正解是李超树,只能默默地学李超树了。

———————————–分界线—————————————

        首先先看看李超树ppt。 这个ppt一开始给了一些线段树的题目,里面的习题的题解如下:
Lucky Queries <-线段树基础
大都市 <-简单的dfs序
堵塞的交通 <-很复杂,很不好想,不好写的线段树
Sum of Medians <-线段树的巧妙运用,提供了更多维护思路
GSS4 <-线段树套路题,poj有差不多的题,写一次以后就都会了
       其实除了堵塞的交通那道题,其他的题目还算好写,感觉这几到题目主要是提供线段树不同角度的思路,顺便让你练练手的ORZ
       那么好,接下来就是李超树的部分了
       首先,李超树能够解决什么问题:n条线段,问你当x=x0时,最大的y是多少
       李超树的本质就是一颗利用了标记永久化的线段树,只不过它维护的东西十分奇特,是“优势线段”,“优势线段”就是从上向下看,出现最长的那段线段就是优势线段
这里写图片描述
        如上图,假如我们此刻维护的区间里面有红蓝这两条线,很明显从上向下看蓝色多一些,那么我们这个区间就维护的是蓝色线段的信息。我们为什么要维护这个东西呢,因为不论x为何值,我们得到的y总是出现在凸壳上。
        然后讨论怎么维护,我们是一条直线一条直线地插入到线段树里面的,当这条直线要更新某个区间的时候,一共有三种情况:
       第一种,此刻区间里面没有可以维护的东西,那么直接更新线段树信息即可
       第二种,新线段与此刻线段树维护的线段没有交点,如果新线段在线段树维护的线段树之上,那么直接更新,否则return
       第三种,新线段与 此刻线段树维护的线段有交点,那么就需要分类讨论了。当交点的x轴坐标在区间正中心的左侧时,如果新线段的斜率高于原线段,那么很明显线段树维护的值就要变成新线段了(上面的图就是这种情况),然后我们继续递归讨论线段树的左儿子,若斜率低于原线段,就直接递归讨论线段树的左儿子即可。当交点位于区间正中心右侧时也是同理,就不赘述了。
        通过上面的讨论,我们也发现一个事情,如果我们想知道x=x0时y的最大值,需要在线段树上完整地跑一次路径才能知道最大值,因为正确答案可能已经被更新到了比较靠下的地方

       最基础的板子题,BZOJ1568,
       代码参考了https://blog.csdn.net/u012288458/article/details/51865920

#include<bits/stdc++.h>
using namespace std;
const int N = 50100;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

double segb[N<<2],segk[N<<2];
bool did[N<<2];

double Intersection(double k1,double b1,double k2,double b2){return 1.0*(b2-b1)/(k1-k2);}

void update(double k,double b,int l,int r,int rt){
    if(!did[rt]) segb[rt]=b,segk[rt]=k,did[rt]=true;
    else{
        double f1=k*l+b,f2=segk[rt]*l+segb[rt],f3=k*r+b,f4=segk[rt]*r+segb[rt];
        if(f1<=f2&&f3<=f4) return ;
        else if(f1>=f2&&f3>=f4) segk[rt]=k,segb[rt]=b;
        else{
            int m=l+r>>1;
            double len=Intersection(k,b,segk[rt],segb[rt]);
            if(f1>=f2){
                if(len<=m) update(k,b,lson);
                else{
                    update(segk[rt],segb[rt],rson);
                    segk[rt]=k; segb[rt]=b;
                }
            }
            else{
                if(len>m) update(k,b,rson);
                else{
                    update(segk[rt],segb[rt],lson);
                    segk[rt]=k; segb[rt]=b;
                }
            }
        }
    }

}


double query(int x,int l,int r,int rt){
    double ans=0;
    if(did[rt]) ans=max(ans,1.0*x*segk[rt]+segb[rt]);
    if(l==r) return ans;
    int m=l+r>>1;
    if(m>=x) ans=max(ans,query(x,lson));
    else ans=max(ans,query(x,rson));
    return ans;
}

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

    }
}

没有更多推荐了,返回首页