• qbzt day3 上午


    内容提要

    lca(最近公共祖先)

    st表

    hash

    并查集

    树状数组

    线段树


     

    数据结构

    1.堆

    Priority_queue

    他滋兹:插入删除查询最大值(最小值)

    分为大根堆小根堆

    2.LCA

    首先我们有一棵树,定义某个点的祖先为这个点到根节点的路径上的所有点

    我们现在有两个点A,B,我们发现A和B有一些公共的祖先

    我们只需要找到最近的公共祖先LCA,就可以找到它们所有的公共祖先

    LCA一定是最深的公共祖先

    步骤:

    1. 如果A的深度小于B的深度,就把它们互换(为了处理方便)
    2. 把A向上调到和B同样的深度,直到A和B同一个深度
    3. A和B一起上调,直到A和B相同

    复杂度 O(deep),如果树是一条链就炸了

    我们考虑优化

    令P[x][i]表示x的第2^i的祖先是哪个节点

    显然P[x][i]=p[[x][i-1]][i-1]

    优化:把A向上调到和B同样的深度

    我们发现A和B之间有深度差

    比如d[A]-d[B]=19     对19进行二进制分解   10011

    A->P[A][4]  16

    A->P[A][1]   2

    A->P[A][0]   1

    优化:A和B一起上调

    我们发现A和B的祖先一直都不相同,直到某个点之后一直相同

    这个点不好确定,所以我们可以确定最后的祖先不相同的地方

    也就是说,i从大往小循环,如果P[A][i]!=P[B][i],就把A和B同时向上跳2^i

     

    LCA应用:常用来处理树上可差分的信息

    比如我们要求点A到点B的路径长度,我们只需要求出LCA(A,B)就好了

     

    ST表

    定义mx[i][j]为i~i+2^j-1的最大值

    如果我们要求[L,R]区间的最大值

    比如[19,46]

    先求L=46-19+1=28

    求log2 L下取整

    然后把区间分成两半求就可以了

    则Ans=max(mx[19][4],mx[31][4])

     

    HASH

    HASH是一种函数,平常说的hash是把一个字符串变成一个数。F(字符串)=int

    Map是基于比较函数的红黑树

    如果你在map中放一个字符串,两个字符串的比较就是O(len(s))的,贼慢

    所以我们需要开发一种新的东西

     

    假如我们用a表示1,b表示0

    ababb 就可以用类似于二进制的方式来算出它的值

     

    Hash是允许冲突的,但是我们要尽可能避免冲突,而不是根治冲突

    Jzm1926 p=83(p比字符串集大并且是个质数)

    ==’6’*p^0+’2’*p^1+....

    但是由于有可能会很大,所以我们要%一个质数

    如果为了方便,可以直接开unsigned long long 因为2^64-1恰好是一个质数(滑稽

    这样自然溢出就省去了取模操作

    为了避免冲突,我们可以考虑取两个模数

    但是我们目前无法求子串的hash值

    我们希望设计一种算法,至少能满足字符串拼接删除

    现在有dingmingshuo这个字符串和一个p

    d     d

    di    d*p^1+i

    din   d*p^2+i*p^1+n

    ding  d*p^3+i*p^2+n*p^1+g

    我们要求ing的hash就是ding的hash-d的hash乘p^3

    ng的hash就是ding的hash-di的hash乘p^2

     

    令h[i]表示1~i的hash值

    h[i]=h[i-1]*p+s[i]

    hash[i][j]=(h[j]-h[i])*p^(j-i+1)

     

    并查集

    并查集是一个集合,可以查询节点是否在一个集合中

    令fa[i]表示x在它所在的树上的父亲,特殊的,根节点的父亲是他自己

    我们要查询a和b是否在一个集合中,只需要不停令他们等于他们的父亲,直到根节点,然后看看根节点是否相等就好了

    如果要把两个集合合并,我们要先找到这两个集合的根,然后令一个的父亲等于另一个

     

    路径压缩:

    我们发现并查集这个东西完全没有必要保留树的结构,所以我们可以让这个集合里面的点的父亲直接指向它的代表源

    int father(int x)
    
    {
    
        return fa[x]==x? x: fa[x]=father(fa[x]);
    
    }

     

     

    树状数组:支持单点修改,前缀查询

    主要应用:线段树常数过大时,线段树功能过多时

     

    注意查询是-lowbit,修改是+lowbit

     

    int lowbit(int x){
        return x&(-x);
    }
    
    void modify(int x,int y){
        // add y to a[x]
        for(int i=x;i<=n;i+=lowbit(i)) c[i]+=y;
    }
    
    int query(int x){
        // sum of a[1]...a[x]
        int ret=0;
        for(int i=x;i;i-=lowbit(i)) ret+=c[i];
        return ret;
    }
    
    int query(int l,int r){
        return query(r)-query(l-1);
    }

     

    树状数组所求的所有问题必须存在逆元

     

    线段树

    支持区间修改,区间查询

    主要应用:一类区间修改区间查询的问题

    结构:

     

     

    单点修改:

    1. 确定点的位置
    2. 更新树的权值

     

    区间查询:

    任何一段线段都可以被log n 条线段表示出来,然后把区间上的信息进行处理就好了

     

    区间修改:

    我们搞一个懒标记,每一次对一个完整的区间节点修改的时候就打上懒标记,然后把懒标记下放                                                                                                                                                     

     

    下放懒标记:

    首先,如果没有懒标记就不需要下放

    然后,把他的两个儿子的懒标记加上父亲的懒标记,在对应的对sum进行修改

    最后,把他的父亲的懒标记删掉

    什么时候下放懒标记?

    什么时候碰到这个节点就下放

     

     

    例题:

    中位数

    维护两个堆,大根堆和小根堆,大根堆n/2+1个元素,小根堆n/2个元素,每次加入两个元素到大根堆里,然后把大根堆的堆顶给到小根堆,这样大根堆的堆顶就是中位数

     

     

     树的重构

    对每一个点算出一个hash,如果两个集合完全相同,就是重构的

    怎么求树的hash?

    对一个节点的hash排序,然后搞一搞

     

    对于一棵无根树,他的重心(所有子树中最大的子树节点数最少)个数不为2

     

    或者枚举每个重心

     

    括号序列,每个封闭的括号代表一个节点。如何把树转换成括号序列?

    递归处理,把点的所有子树的括号序列按照字典序排序,括号序列字典序最小的就再加一个括号

  • 相关阅读:
    MySQL之索引优化
    使用Nginx+Lua(OpenResty)开发高性能Web应用
    Eclipse设置背景色
    删除排序数组中的重复项再练习
    计数排序_数组与集合时间比较
    nodejs+redis应用
    redis的一些优缺点
    Redis的线程模型
    GC仅仅是守护线程,空闲执行
    SpringIOC和AOP的生活案例
  • 原文地址:https://www.cnblogs.com/lcezych/p/11188248.html
Copyright © 2020-2023  润新知