小囧了一下,双连通分量有两种我是前两天才知道的
相关概念参考byv牛的blog,这里记几个关键的点
桥就是dfn[i] < low[j]的边(i, j)
割点就是它发出去的边(i, j)中至少一条满足dfn[i] <= low[j](这里是要取等号的)
我一般求的是所谓 边双连通分量,实际上就是把桥去掉之后还在一起的点的集合
而这里还有一个 点双连通分量,这个东西实际上就是差不多把割点去掉之后还在一起的边的集合
ps:点 <=> 边,这个东西为什么不反过来叫呢?
求法的话差不多,都是用tarjan算法,不过求边双连通分量时还要开一个边的栈,碰到一条满足dfn[i] <= low[j]的边就退一次栈一直退到这条边
还有一个东西就是那个求LCA的算法
它用到的主要思想就是倍增,求出每个点往上跳2^j步后的点,从而使一个点可以以O(logn)的复杂度跳到它的某个祖先跟前
但是怎么每次询问具体怎么做呢?
第一个简陋的想法就是二分lca的深度,再跳,再判——但是这样一来,每次询问的复杂度就达到了O((logn)^2),不太理想
有直接logn的方法吗?显然是有的,先让询问的点对(i, j)中深度大的先跳到两个深度一样的地方,再两个一起跳,这样就是O(logn)的复杂度了,不过常数乘了个2
有只跳一轮的方法吗?暂时还没想到
另外,我为什么在这里提一下这个并不是特别漂亮的LCA的方法呢?
尽管这个方法无论是时间还是空间都不是最优的,但它有一个巨大的优势,在线和可扩展
ghy大牛论文中利用欧拉序列求lca的方法难以扩展,无法在求lca的同时求一些附加信息诸如路径上的权值和之类的
有一个离线O(n+q)利用了并查集的方法虽然可扩展性比较强,可是却是离线的
还有一点,这个方法所需求的信息很少,仅用父亲表示法的树就能求出所有东西,而其他的方法都需要把树用邻接表存下来才行
这个优势其实是相当有用的——在一些“扩展版”的树中要求lca的时候这个方法就有着编程复杂度小的优势了
比方说在cactus图(仙人掌图,图中的每条点最多属于一个简单环)中,或是yt的最短路那道题中的每条边最多属于一个简单环的图,如果把一个点能扩展出的块中的点的父亲都改成它,最后这个图就成了一棵树
我们实际上比较容易求出的是最后那棵树的父亲表示法,如果再要重建一个邻接表无疑就麻烦了,这个时候用这个方法求lca就很方便了
附集训队yt-最短路的代码,包含了上面说的大部分算法