题意
询问任意两点相互到达,要添加几条边
思路:
- tarjan缩点
- 注意tarjan缩点后形成的树
- 属于同一个强连通分量的low相同
- 竟需将树中所有入度为1的强连通分量连接即可
- 注意应为upper_bound(deg[1] / 2) 及 (ans + 1 ) >> 1
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
#define MAX_V 6000
#define mem(a,b) memset(a,b,sizeof(a))
vector<int> G[MAX_V];
int ord,V;
int dfn[MAX_V],low[MAX_V];
int stk[MAX_V],top;
int instack[MAX_V];
int deg[MAX_V];
bool A[MAX_V][MAX_V];
void addedge(int from,int to)
{
G[from].push_back(to);
G[to].push_back(from);
}
void tarjan(int u,int fa){
dfn[u] = low[u] = ++ord;
instack[u] = 1;
for (int i = 0 ; i < G[u].size() ;i++ ) {
int v = G[u][i];
if (v == fa) continue;
if (dfn[v] == -1){
tarjan(v,u);
low[u] = min(low[u],low[v]);
} else if (instack[v]){
low[u] = min(dfn[v],low[u]);
}
}
}
void init(){
top = ord = 0;
for (int i = 0;i <= V ; i++) {
G[i].clear();
}
mem(stk,0);
mem(low,0);
mem(deg,0);
mem(instack,0);
mem(dfn,-1);
mem(A,0);
}
int main()
{
int M;
while(scanf("%d%d",&V,&M) == 2) {
init();
for (int i = 0;i < M ;i++){
int u,v;
scanf("%d%d",&u,&v);
if (!A[u][v]){
addedge(u,v);
A[u][v] = A[v][u] = true;
}
}
tarjan(1,-1);
for (int u = 1;u <= V; u++ ) {
for (int i = 0;i < G[u].size() ;i++) {
int v = G[u][i];
if (low[v] != low[u]){
deg[low[v]]++;
}
}
}
int ans = 0;
for (int i = 1;i <= V;i++){
if (deg[i] == 1) ans++;
}
printf("%d\n",(ans + 1)>>1);
}
return 0;
}