模板大部分来自LRJ
连通性
割点
<pre name="code" class="cpp">//带重边处理
int tarjan(int u, int fa)
{
bool f = false;
int lowu = dfn[u] = ++dfs_c;
REP(i, G[u].size())
{
Edge& e = edges[G[u][i]];
if (e.to == fa && !f) ///第一次为访问父节点不算, 其他则为重边
{
f = 1;
continue;
}
if (!dfn[e.to])
{
int lowv = tarjan(e.to, u);
lowu = min(lowu, lowv);
if (lowv > dfn[u])
{
edges[G[u][i]].flag = true;
edges[G[u][i] ^ 1].flag = true;
}
}
else
lowu = min(lowu, dfn[e.to]);
}
low[u] = lowu;
return lowu;
}
vector<int> G[MAXN];
int pre[MAXN], low[MAXN], iscut[MAXN];
///pre[u]表示dfs中u的次序编号
///low[u]表示u及u的子树中能追溯到的最早的节点的次序编号值(即pre值)
///iscut[u]表示u是否为割点
int dfs_clock = 0;
///调用前需将pre数组赋0,首次调用fa为-1
int dfs(int u, int fa)///fa表示u的父节点
{
int lowu = pre[u] = ++dfs_clock;
int child = 0; ///表示子节点个数
for (int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if (!pre[v]) ///若没被访问
{
int lowv = dfs(v, u);
lowu = min(lowv, lowu); ///用子节点的low值更新lowu
if (lowv >= pre[u] && fa >= 0)
{
iscut[u] = 1;
}
}///若被访问,则用反向变(返祖边)更新当前u的low值
else if (v != fa && pre[v] < pre[u])///切记v != fa
{
lowu = min(lowu, pre[v]);
}
}
if (fa < 0 && child > 1) ///根节点为割点当且仅当子节点数大于一个
iscut[u] = 1;
low[u] = lowu;
return lowu;
}
求删割点后剩余联通分量数时,只用将iscut[u] = 1改为iscut[u]++,则非根节点剩余分量数为iscut[u]+1,根节点为iscut[u]
(若u为割点,记iscut[u]为u的子节点数,则去掉u后,图被分成iscut[u]+1个部分(每个子节点的部分和u的祖先的部分),若u为dfs树的根,则分成iscut[u]个部分(根节点没有祖先))
点双连通分量
<pre name="code" class="cpp">//当u为割点时,把边从栈顶依次取出,直到遇到边(u,v),取出的边与其关联的点,组成点双连通分支。
//割点可以属于多个点双连通分支,其余点和每条边只属于且属于一个点双连通分支
//基本算法与求割点一致
int dfn[MAXN], iscut[MAXN], bccno[MAXN];
int dfs_clock, bcc_cnt;
vector<int> G[MAXN], bcc[MAXN];
struct Edge{
int u, v;
};
stack<Edge> S;
int dfs(int u, int fa)
{
int lowu = dfn[u] = ++dfs_clock;
int child = 0;
for (int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
Edge e = (Edge){u, v};
if (!dfn[v])
{
S.push(e);
child++;
int lowv = dfs(v, u);
lowu = min(lowu, lowv);
if (lowv >= dfn[u])
{
iscut[u] = 1;
bcc_cnt++;
bcc[bcc_cnt].clear();
while (1)
{
Edge x = S.top();
S.pop();
if (bccno[x.u] != bcc_cnt)
{
bcc[bcc_cnt].push_back(x.u);
bccno[x.u] = bcc_cnt;
}
if (bccno[x.v] != bcc_cnt)
{
bcc[bcc_cnt].push_back(x.v);
bccno[x.v] = bcc_cnt;
}
if (x.u == u && x.v == v)///最后才判断跳出,因为(u,v)这条变也要加入
break;
}
}
}
else if (v != fa && dfn[v] < dfn[u])
{
S.push(e);
lowu = min(lowu, dfn[v]);
}
}
if (fa < 0 && child == 1)
iscut[u] = 0;
return lowu;
}
void find_bcc(int n)
{
memset(dfn, 0, sizeof(dfn));
memset(iscut, 0, sizeof(iscut));
memset(bccno, 0, sizeof(bccno));
dfs_clock = bcc_cnt = 0;
for (int i = 1; i <= n; i++)
if (!dfn[i])
dfs(i, -1);
}
边双连通分量
struct Edge{
int from, to;
bool flag;
};
vector<Edge> edges;
VI G[maxn];
int dfn[maxn], bccno[maxn], out[maxn];
int dfs_c, bcc_cnt, n, m;
bool vis[maxn];
inline void add(int x, int y)
{
edges.PB((Edge){x, y, false});
edges.PB((Edge){y, x, false});
int sz = edges.size();
G[x].PB(sz - 2);
G[y].PB(sz - 1);
}
int tarjan(int u, int fa)
{
int lowu = dfn[u] = ++dfs_c;
REP(i, SZ(G[u]))
{
Edge& e = edges[G[u][i]];
if (!dfn[e.to])
{
int lowv = tarjan(e.to, u);
lowu = min(lowu, lowv);
if (lowv > dfn[u])
{
e.flag = true;
edges[G[u][i] ^ 1].flag = 1;
}
}
else if (dfn[e.to] < dfn[u] && e.to != fa)
lowu = min(lowu, dfn[e.to]);
}
return lowu;
}
void find_bcc()
{
CLR(dfn, 0), CLR(bccno, 0);
CLR(out, 0);
dfs_c = bcc_cnt = 0;
FE(i, 1, n)
if (!dfn[i])
tarjan(i, -1);
}
void init()
{
FE(i, 0, n + 1)
G[i].clear();
edges.clear();
CLR(vis, 0);
}
void dfs(int u, int cnt)
{
vis[u] = 1, bccno[u] = cnt;
REP(i, SZ(G[u]))
{
Edge& e = edges[G[u][i]];
if (e.flag)
{
//printf("Bridge: %d %d\n", e.from, e.to);
continue;
}
if (!vis[e.to])
dfs(e.to, cnt);
}
}
void solve()
{
FE(i, 1, n)
if (!vis[i])
{
bcc_cnt++;
dfs(i, bcc_cnt);
}
REP(i, SZ(edges))
{
int u = edges[i].from, v = edges[i].to;
if (bccno[u] != bccno[v])
out[bccno[u]]++;
}
int leaf = 0;
FE(i, 1, bcc_cnt)
if (out[i] == 1)
leaf++;
//cout << bcc_cnt << " " << leaf << endl;
int ans = (leaf + 1) / 2;
if (bcc_cnt == 1) ans = 0;
WI(ans);
}
int main()
{
int x, y;
while (~RII(n, m))
{
init();
REP(i, m)
{
RII(x, y);
add(x, y);
}
find_bcc();
solve();
}
}
重边处理
//对于有重边的图,求边双连通分量
struct EDGE
{
int u, v;
int next;
};
int first[MAXN], rear;
EDGE edge[MAXE];
void init()
{
memset(first, -1, sizeof(first));
rear = 0;
}
void insert(int tu, int tv)
{
edge[rear].u = tu;
edge[rear].v = tv;
edge[rear].next = first[tu];
first[tu] = rear++;
edge[rear].u = tv;
edge[rear].v = tu;
edge[rear].next = first[tv];
first[tv] = rear++;
}
int pre[MAXN], low[MAXN];
bool vis_e[MAXE];
bool is_cut_e[MAXE];
int dfs_clock;
int dfs(int cur, int fa)
{
pre[cur] = low[cur] = ++dfs_clock;
int child(0);
for(int i = first[cur]; i != -1; i = edge[i].next)
{
int tv = edge[i].v;
if(!pre[tv])
{
++child;
vis_e[i] = true;
vie_e[i^1] = true;
st.push(i);
int lowv = dfs(tv, cur);
low[cur] = min