求双连通分量,利用并查集缩点,形成一棵树,树边肯定都是桥,然后每对点x,y,找原图中x,y点对应的新图中的点,如果不是一个点,则向上找它们的LCA,因为它们之间连了一条边,所以这些点到它们的LCA之间的边都不是割边了,找LCA时,先将两点上升到同一层次,然后一起再向上找父亲节点,其间遇到桥就把桥的标记删除,并且答案减1。
这个题比上一个好玩多了。
#include <iostream> using namespace std; const int MAXN = 100005; struct node { int to, next; } edge[MAXN * 4]; int head[MAXN], dfn[MAXN], low[MAXN], pre[MAXN], vis[MAXN]; int n, m, timee, cnt, cut; bool bi[MAXN]; void init() { memset(vis, 0, sizeof(vis)); memset(head, -1, sizeof(head)); memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); for (int i = 0; i <= n; pre[i] = i, i++) ; memset(bi, 0, sizeof(bi)); cnt = 0; cut = 0; timee = 1; } void addedge(int u, int v) { edge[cnt].to = v; edge[cnt].next = head[u]; head[u] = cnt; cnt++; } void dfs(int u, int fa) { dfn[u] = timee; low[u] = timee; timee++; vis[u] = 1; for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (!vis[v]) { dfs(v, u); pre[v] = u; //记录父节点 low[u] = min(low[u], low[v]); if (low[v] > dfn[u]) { //如果子节点的low值大于父节点的时间戳这就是桥 cut++; bi[v] = true; } } else if (vis[v] == 1 && v != fa) { low[u] = min(low[u], dfn[v]); } } vis[u] = 2; } int judge(int u, int v) { int cnt1 = 0; while (dfn[u] > dfn[v]) { if (bi[u]) { cnt1++; bi[u] = false; } u = pre[u]; } while (dfn[u] < dfn[v]) { if (bi[v]) { cnt1++; bi[v] = false; } v = pre[v]; } while (u != v) { if (bi[u]) { bi[u] = false; cnt1++; } if (bi[v]) { bi[v] = false; cnt1++; } u = pre[u]; v = pre[v]; } return cnt1; } int main() { int u, v, q, in = 0; while (scanf("%d%d", &n, &m), n + m) { if (in) { puts(""); } in++; init(); for (int i = 0; i < m; i++) { scanf("%d%d", &u, &v); u--; v--; addedge(u, v); addedge(v, u); } dfs(0, 0); scanf("%d", &q); printf("Case %d:\n", in); for (int i = 0; i < q; i++) { scanf("%d%d", &u, &v); u--; v--; cut -= judge(u, v); printf("%d\n", cut); } } return 0; }