day16 堆&并查集

堆与并查集


  • 一棵树,满足每个节点的权值都大于等于/小于等于其叶子节点的权值

  • 根节点小的叫小根堆,否则叫大根堆

  • 一些堆还能高效的进行 merge 、可持久化等操作

  • 向上调整:如果这个节点的权值大于它父亲的权值,就叫换,重复此过程直到不满足或者到根节点
  • 复杂度 O(\log n)
  • 增加某个点的权值:直接修改后,向上调整一次,但 priority_queue 不支持直接修改
  • 向下调整:在该节点的儿子中,找一个最大的,与该节点交换,重复此过程直到底层
  • 复杂度 O(\log n)
  • 插入节点向上调整,删除节点向下调整

  • 建堆:从一个空的堆开始,插入 n 个元素
  • 每次都逐个向下调整
  • 复杂度 O(\log n-k)

维护一个序列,支持两种操作:

  • 向序列中插入一个元素
  • 动态维护一个序列上第 k 大的数
  • 可以使用对顶堆解决

  • 由一个大根堆与一个小根堆组成

  • 小根堆维护大值即前 k 大的值(包含第 k 个)

  • 大根堆维护小值即比第 k 大数小的其他数

并查集

  • 用于管理元素所属集合的数据结构,其本质是一棵树
  • 合并(Union):合并两个元素所属集合
  • 查询(Find):查询某个元素所属集合
  • 优化:路径压缩、按秩合并
  • 时间复杂度最坏为 O(m\log n) ,平均为 O(m\alpha(m,n)) ,其中 m\alpha 为反阿克曼函数(在优化的情况下)
  • n 个点,初始时均为孤立点
  • 接下来有 m 次加边操作,第 i 次操作在 a_ib_i 之间加一条无向边。设 L(i,j) 表示结点 ij 最早在第 L(i,j) 次操作后连通
  • m 次操作完后,你要求出 \sum^n_{i=1}\sum^n_{j=i+1}L(i,j) 的值
  • 并查集记录子树的大小。考虑统计每次操作的贡献。如果第 i 操作 a_ib_i 分属于两个不同子树,就将这两个子树合并,并将两者子树大小的乘积乘上 i 累加到答案里。
  • n 个点,初始时均为孤立点
  • 接下来有 m 次加边操作,第 i 次操作在 a_ib_i 之间加一条无向边
  • 接下来有 q 次询问,第 i 次询问 u_iv_i 最早在第几次操作后连通
  • 考虑在并查集合并的时候记录并查集生成树,也就是说如果第 i 次操作 a_ib_i 分属于两个不同子树,那么吧 (a_i,b_i) 这条边纳入生成树中,边权是 i 。那么查询就是询问 uv 路径上边权的最大值,可以使用树上倍增或树链剖分的方法维护
  • n 个点,初始时均为孤立点
  • 接下来有 m 次加边操作,第 i 次操作在 a_ib_i 之间加一条无向边
  • 接下来有 q 次询问,第 i 次询问第 x_i 个点在第 t_i 次操作后所在连通块大小
  • 当然可以写个可持久化并查集
  • 离线做法:考虑将询问按 t_i 从小到大排序。在加边的过程中顺便处理询问即可。时间复杂度 O(q\log q+(n+q)\alpha(n))
  • 如果强制在线呢?
  • 我们只需要求出 x_i 在重构书中最大的一个连通块使得连通中的点权最大值不超过 t_i ,询问的答案按就是这个连通块中点权为 0 的节点个数,即叶子节点个数。
  • 由于操作的编号是递增的,因此重构树上父节点的点权总是大于子节点的点权
  • 这意味着可以在重构树上从 x_i 到根节点的路径上倍增找到点权最大的不超过 t_i 的结点。
  • 时间复杂度 O(n\log n)
  • 给一个长度为 n\texttt{01} 序列 a_i\dots a_n ,一开始全是 0 ,接下来进行 m 次操作
  1. a_x=1
  2. a_n,a_x + 1,\dots a_n 中左数第一个为 0 的位置
  • 建立一个并查集, f_i 表示 a_i,a_{i+1},\dots,a_n 中第一个 0 的位置,初始化 f_i=i
  • 对于一次 a_x=1 的操作,如果 a_x 已经等于 1 则跳过,否则令 f_x=f_{x+1}
1 个赞