双联通图:就是一个图中任意两点之间都有两条不重合的路径相连。
桥:指的是强联通分量之间的边。
将一个无向图变成一个双联通图所需的最小边为:
首先将该图缩点,缩完点之后的图就是一个树,设该树的叶子节点为x;
所需边数:(x + 1)/ 2;
cpp
int dfn[N], low[N], timestamp;
int stk[N], id[N], all[N];
bool in_stk[N];
int h[N], e[N], ne[N], idx;
int n, m, top, scc_cnt;
int du[N];
bool is_bridge[N];
inline void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void tarjan(int u, int from)
{
// 记录序号以及初始化可以到达的最小标号点
dfn[u] = low[u] = ++ timestamp;
stk[++ top] = u;
// 便利当前节点的所有节点
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(!dfn[j]) // 该点未遍历
{
tarjan(j, i); // 便利子节点
low[u] = min(low[u], low[j]); // 更新当前节点可以到达的最小序号点
if(dfn[u] < low[j]) // 表明是一个桥
is_bridge[i] = is_bridge[i ^ 1] = 1;
}
else if(i != (from ^ 1))
low[u] = min(low[u], low[j]);
}
if(dfn[u] == low[u])
{
int y;
++ scc_cnt;
do
{
y = stk[top --];
id[y] = scc_cnt;
}while(y != u);
}
}
inline void sovle()
{
cin >> n >> m;
memset(h, -1, sizeof h);
while(m --){
int a, b;
cin >> a >> b;
add(a, b);
add(b, a);
}
tarjan(1, -1);
for(int i = 0; i < idx; i ++)
if(is_bridge[i]) // 将该点的出度加一
du[id[e[i]]] ++;
int s = 0;
for(int i = 1; i <= scc_cnt; i ++)
if(du[i] == 1) // 若是该点的出度为1,说明是一个叶子节点
s ++;
cout << (s + 1) / 2 << endl;
}