题目大意]
- 给定一张图,求最少加入几条边可以使得整个图成为一个边双连通图。
- 具体的说也是非常简单:求双联通分量,染色,缩点,然后求度为1的点数,答案就是(这个数+1) Div 2。
- 其他的就比较简单了,按照Low和Dfn的定义,如果某个点拓展完后Low=Dfn,那么栈中从栈顶到这个店为止的部分都属于同一个双联通分量。这个由Dfs树的性质决定的。这样求出来之后,只需要对这些点进行染色,不需要真的缩点,只要再去重新枚举原图中的边,向新的颜色不同的顶点之间加边即可。又由于双联通分量的定义,这样新的图一定是一个树,如果不是一个树,那有环的部分按照定义是应该在同一个双联通分量中的。然后求求度数,计算输出即可。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define maxn 5005
#define maxm 40005
struct Edge
{
int v, next;
} edge[maxm];
int n, m;
int head[maxn];
int ecount, tcount;
int dfn[maxn], vis[maxn], low[maxn], ans1,degree[maxn],vis1[maxn];
void addedge(int a, int b)
{
edge[ecount].v = b;
edge[ecount].next = head[a];
head[a] = ecount;
//hash[a][b] = hash[b][a] = true;
ecount++;
}
void input()
{
memset(head, -1, sizeof(head));
memset(vis1,0,sizeof(vis1));
ecount = 0;
int i,j;
ans1=0;
scanf("%d%d", &n, &m);
for (i = 0; i < m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
a--;
b--;
vis[a]=vis[b]=1;
addedge(a, b);
addedge(b, a);
}
for(i=0;i<n;i++)
{
if(!vis[i])
{
ans1++;
}
}
}
void dfs(int fa, int u)
{
vis[u] = true;
low[u] = dfn[u] = tcount++;
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if (v == fa)
continue;
if (!vis[v])
dfs(u, v);
low[u] = min(low[u], low[v]);
}
}
int tarjan()
{
memset(dfn, 0, sizeof(dfn));
memset(vis, 0, sizeof(vis));
memset(degree, 0, sizeof(degree));
tcount = 0;
dfs(0, 0);
int ret = 0;
for (int i = 0; i < n; i++)
for (int j = head[i]; j != -1; j = edge[j].next)
{
int v = edge[j].v;
if (low[i] != low[v])
degree[low[i]]++;
}
for (int i = 0; i < n; i++)
if (degree[i] == 1)
ret++;
ret=(ret+1)/2;
if(ret)ret+=ans1;
else
ret=ans1+(ans1?1:0);
return ret;
}
int main()
{
//freopen("t.txt", "r", stdin);
int t;
cin>>t;
while(t--)
{
input();
int ans=0;
ans = tarjan();
printf("%d\n", ans);
}
return 0;
}