zoukankan      html  css  js  c++  java
  • 长链剖分学习笔记

    前置技能

    ​ 学这个之前应该要比较熟悉重链剖分,推荐一下这篇博客

    一些性质

    ​ 我们类比重链剖分,定义每个点所有儿子中,子树深度最大的点为它的重儿子,那么整棵树就被划分成了一些不相交的重链,然后首先就有一个性质那就是所有重链长度和是(O(n))级别的,这个东西很显然,还有一个性质就是一个点的k级祖先所在重链长度一定大于等于k,这个东西考虑反证法,假设小于k,那么当前点显然会和这个点在一条重链上,然后就没了。

    具体应用

    求k级祖先

    ​ 用长链剖分可以在(O( ext{n log n})-O(1))的时间内在线回答一个点的k级祖先,首先我们预处理出每个点的第(2^i)个祖先,以及每一条重链的链顶向上的链长个祖先与向下链长个重儿子分别是什么,还有每个数二进制最高位highbit是哪一位,预处理这些东西的复杂度是(O( ext{n log n}))的,瓶颈在预处理每个点的第(2^i)个祖先上。那么对于询问x点的k级祖先,我们先跳到x的第(2^{ ext{highbit(k)}})个父亲上,那么这个时候当前点到离k级祖先的距离是小于(frac{k}{2})的,我们再跳到当前链的链顶,判断k级祖先在当前点上面还是下面即可,根据性质二,这条长链长度是大于等于(frac{k}{2})的,这样就可以(O(1))回答询问了。

    ​ 1.例题 lxhgww的奇思妙想,这个是模板题就不讲解了。

    优化以深度为下标的DP

    ​ 这个思想有点像启发式合并,大概是直接继承重儿子的dp值,然后轻儿子暴力合并,因为总链长是(O(n))的,所以转移的复杂度就是(O(n))的,一般来说我们可以利用数组指针来完成这个操作,把重儿子的数组指针设为当前点的$pm$1,给剩下的轻儿子分配剩余的内存就好了。

    ​ 1.例题 [POI2014]Hotel加强版,题意是统计有多少种方案可以从树上选出3个点使得它们两两间距离相等。

    ​ 我们首先来考虑如何统计点,有两种情况,一种是都在一个点的子树内,第二种是两个点在子树内,另一个点在子树外,我们分开来讨论这两种情况,设(f[x][j])表示x子树内距离(x)(j)的点数,(g[x][j])(x)子树内有多少对点((u,v))满足(dis(u,x)=dis(v,x)=d),且(dis(u,v)=d+j),那么我们有以下转移:

    [f[u][j]=sum_vf[v][j-1]\g[u][j]=sum_vg[v][j+1]+f[u][j] imes f[v][j-1]\ans=sum_{k}sum_usum_vf[u][k] imes g[v][k+1]+g[u][k] imes f[v][k-1]$$​ ​ 注意转移顺序,复杂度$O(n^2)$。 ​ 我们发现这个DP是以深度为下标的,那么我们对这棵树进行长链剖分,对于重儿子我们直接用指针偏移,让$f[x]=f[son[x]]-1,g[x]=g[son[x]]+1$,由于我们是先访问父亲的,所以移下项就好了,然后注意因为$f$是不断往后面偏移的,$g$是不断往前面偏移的,所以每次给一个点分配空间就要开两倍重链长,$f$和$g$分别定位在两端,这样让$f$不断往前偏移,$g$不断往后偏移,这样子就不会有空间被共用了,复杂度$O(n)$,[代码戳我](https://paste.ubuntu.com/p/4q8DFG6Np3/)。 ​ 2.例题 [[WC2010]重建计划](https://www.luogu.org/problemnew/show/P4292),题意是给你一颗带边权的树,要求你找一条路径满足路径长度在$[L,R]$之间,并且最大化路径权值和与路径长度之比。 ​ 对于分数规划我们套路是二分答案,把分母乘过去那么一个答案可行条件为:$$sum val-mid imes lenge0]

    ​ 那么我们把每条边边权都减去(mid),那么只需要存在一条长度和大于等于(0)的路径即可,我们利用:$$dis(x,y)=dep(x)+dep(y)-dep(lca(x,y))*2$$

    ​ 在这里(dep(x))是从根节点到x点的路径权值和。

    ​ 那么我们只要在(lca)处更新答案就好了,我们记(f[i][j])为在(i)号点子树内与(i)号点距离为(j)的点的dep最大值,这样子直接做是(O(n^3 ext{log n}))的,我们把这个值放到线段树里面那么就把一个(n)优化成了(log),再用长链剖分每次直接继承重儿子的值又优化了一个(n),那么复杂度就到了(O( ext{n log}^2 ext{n})),在这里我们用的不是数组的偏移,而是DFS序来实现对重儿子的继承,因为一条重链的(dfs)序是连续的,我们定义(g[id[i]+j])(i)号点子树内与(i)号点距离为(j)的点的dep最大值,每次更新的时候同时在线段树上更新就好了,和之前那个题一样的,要注意转移的顺序应该是先更新答案再更新线段树里的值,代码戳我

    总结

    ​ 其实用的比较多的还是优化DP,这类DP特征也很明显就是以深度为下标,做题的时候注意一下就好了,如果不是以深度为下标的话用启发式合并也可以做到很优秀的一个(log)的复杂度,其实最重要的还是推和树上距离有关的转移方程,只要方程能写出来优化也都是比较套路的东西了。

  • 相关阅读:
    abstract关键字
    final关键字
    Vue使用枚举类型实现HTML下拉框
    第八节 pandas读取和保存文件
    第七节 pandas新建数据框的两种方式
    第六节 numpy的常用属性和方法
    第五节 numpy的简单使用
    第三节 matplotlib绘制直方图
    第三节 matplotlib绘制条形图
    第二节 matplotlib绘制散点图
  • 原文地址:https://www.cnblogs.com/brunch/p/10241466.html
Copyright © 2011-2022 走看看