[题意]一个无向图可以有重边,下面q个操作,每次在两个点间连接一条有向边,每次连接后整个无向图还剩下多少桥(每次回答是在上一次连边的基础之上)
[分析]好题,做完后涨了很多姿势~
普通做法当然就是每加一条边重新算一次桥,但这样复杂度将达到O(q*M),显然要超时。。所以我们需要“动态地”在原图的基础上求桥~
我们可以先把图求一次边双连通分量(BCC)然后缩点,因为同一双连通分量中没有桥,加边没有影响。一个很重要的性质就是
{一个图求一次边双连通分量缩点后将变成一颗树或者森林,并且树中的每条边都是桥}。因为此题中说所有的点都有边连着,所以这里缩点后是一棵树。
显然,树中每添加一条边,就会形成一个环,而这个环中的边将不再是桥,并且他们构成新的边双连通分量,所以我们每次在桥中减去这些边,并把他们缩成一个点。
第一,
怎么求在树中加边(u,v)后形成的环?
我们可以求出u,v的LCA,然后环就是v->LCA(u,v)->u>(u,v)->v.
第二,
怎么缩点?
以前一直做的是“静态缩点”,就是求一遍边BCC后图的结构就不变了,此时我们可以在求出每个点所属的BCC(bcc[i])后以BCC的标号来代替缩后的点。如果我们动态地加边,则每次都要修改原BCC中所有的点成新BCC,所以直接这样改行不通。这里我们是不是发现它很像……并查集?对!就是用并查集维护bcc[]!这是我做这道题学到的最重要的姿势
:用并查集维护动态加边的缩点。
当然此题中我们没有用到bcc[],因为在求LCA时我们用的朴素的方法:{用level[]表示每个节点的深度,两个点同时向根爬,深度深的节点先爬(LCA(u,v) = LCA(u, father[v]) ),深度相同时两个点一起爬(LCA(u, v) = LCA(father[u], father[v]) ),直到两个点相同。}这种方法的好处是可以一边求LCA一边缩点。那么我们就需要一个father[]来维护节点的父节点。这样我们就不需要bcc[]了,直接用并查集维护father[],并用它表示缩点及所属BCC。
#include
#include
#include
#include
#include
#include
#include
#include