zoukankan      html  css  js  c++  java
  • [BZOJ1602&BZOJ1787&BZOJ2144]树上LCA的算法巩固练习

    简述求LCA的倍增算法

      对于树上的所有节点,我们可以很轻松地通过dfs求出其直接的父亲节点以及其深度

      通过类似RMQ的原理我们可以处理出每个节点的第2^i个父亲

      //这个过程既可以在dfs之后双重循环建也可以像树剖模板里那样dfs里直接建

      //个人比较推荐后者,会少掉一些不必要的运算,但由于log算法的优越性使得它们实际差别不大

      如图u,v为题目中给的两个节点,我们要做的第一步是将u,v调整到同一深度

      做法很简单,只需要用2^i从大到小逼近答案

      调整到同一深度以后两个节点共同前进,做法和上面调整深度时一样

      细节:

        当u,v的深度刚开始就相同时一定要特判,因为运算到ln(0)会出错


    BZOJ1602 简单的LCA模板题

    BZOJ1787 题目大意是让我们求出树上三个点到同一点的边权加和最小,输出那个点和最小边权


      我们通过上面的例子可以发现,并不是三点的LCA就是边权和最小的

      因为图上的红边如果走到红色节点的时候会走两次,而走到绿色节点时只走一次,其他路径上的节点各一次

      发现其实和树的重心有点关系...然后就想到了暴力滚粗的ZJOI Day1T1

      其实这道题没那么麻烦...因为只有三个点,很容易想到最终的答案一定是其中两个点的LCA

      那么枚举三次就可以了,先将其中两个点做一次LCA,求出路径边权和,再将这个新求出的点和剩下的点做LCA,求路径和

      第一问的答案就是第一次LCA后求出的那个点

    BZOJ2144

      几乎看不出来和LCA有什么关系的题...

      但是想出来了之后觉得这个思路简直太好了...

      对于一个状态我们用三元组表示(x,y,z)

      一种转移是从外面的点跳向中间,而显然由于题目中“只能跳过一颗棋子”的限制所以只有一种方法

      而从中间的点跳向两边就有两种方式

      按照以前的思路,这里就直接BFS敲起来了..

      我们考虑每一个三元组不停地向中间跳一定有一个最终状态

      而这个最终状态向外跳又能产生一系列形如二叉树的状态

      我们将向外跳定义为向儿子状态的连边,向上条定义为向父亲节点的连边

      两个状态的树上路径长度正好的题目中要求的内容

      而第一问只需判断根节点的状态是否一样就可以了

      但对于10^9的数据,想到这里显然还不够

      我们面临着两个问题,一个是depth怎么求(数组根本开不下状态也枚举不完),第二个是第2^i个父亲怎么求

      我们发现这两个问题是有联系的

      考虑一个状态(x,y,z),设t1=y-x,t2=z-y

      当t1>t2时,显然是右边的z往左跳,但是可以跳几步呢?我们解不等式即可得出:(t1-1)div t2步

      当t2>t1时同理

      每次更新t1,t2我们发现它实际上是一个辗转相除的过程,也就是没有几步就可以到达根节点

      也可以根据这个过程叠加出深度

      也可以根据这个过程,算出已知状态的第2^i个父亲状态

      这样一来,这个问题就差不多解决了

      最后一个细节,读进来的状态是无序的,要排序后再做/w

  • 相关阅读:
    ActionBarSherlock学习笔记——知识点
    ActionBarSherlock学习笔记——SubMenu
    Android解析xml——pull
    ActionBarSherlock学习笔记——ShareActionProvider
    Listview下拉刷新(顶部刷新)
    ActionBarSherlock学习笔记——SupportActionBar()属性设置
    ActionBarSherlock学习笔记——ActionBar.Tab
    ActionBarSherlock学习笔记——SearchView
    ActionBarSherlock学习笔记——requestWindowFeature
    ActionBarSherlock学习笔记——ActionBar添加View
  • 原文地址:https://www.cnblogs.com/mjy0724/p/4428072.html
Copyright © 2011-2022 走看看