题目链接

Tarjan+LCA

Tarjan缩点之后的图是一颗树,树上所有的边都是桥,在树上连边,两端点之间所有的边就是要减去的桥。

LCA能在log的时间复杂度记录树上两端点路径上的边。
因为一个子节点只有一条边连向自己的父节点,记录边可以直接记录子节点。

#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>

using namespace std;

typedef long long ll;

const int Maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
const int Mod = 1e9+7;

struct Edge {
    int v, next;
} edge[Maxn<<2], sedge[Maxn<<2];

int h[Maxn], edge_cnt, sh[Maxn], sedge_cnt;
int scc, Stack[Maxn], Top, indx;
int dfn[Maxn], low[Maxn], num[Maxn], pre[Maxn], deep[Maxn];
bool vis[Maxn];

void add(int u, int v) {
    edge[edge_cnt].v = v;
    edge[edge_cnt].next = h[u];
    h[u] = edge_cnt++;

    edge[edge_cnt].v = u;
    edge[edge_cnt].next = h[v];
    h[v] = edge_cnt++;
}

void sadd(int u, int v) {
    sedge[sedge_cnt].v = v;
    sedge[sedge_cnt].next = sh[u];
    sh[u] = sedge_cnt++;

    sedge[sedge_cnt].v = u;
    sedge[sedge_cnt].next = sh[v];
    sh[v] = sedge_cnt++;
}

void dfs(int u, int fa) {
    dfn[u] = low[u] = ++indx;
    Stack[Top++] = u;
    bool ok = false;
    for(int i = h[u]; i != -1; i = edge[i].next) {
        Edge e = edge[i];
        if(e.v == fa && !ok) {
            ok = true; continue;
        }
        if(!dfn[e.v]) {
            dfs(e.v, u);
            low[u] = min(low[u], low[e.v]);
        } else if(e.v != fa || ok) low[u] = min(low[u], dfn[e.v]);
    }

    if(dfn[u] == low[u]) {
        scc++;
        int v;
        do {
            v = Stack[--Top];
            num[v] = scc;
        } while(u != v);
    }
}

void num_dfs(int u, int fa, int step) {
    pre[u] = fa; deep[u] = step; vis[u] = true;
    for(int i = sh[u]; i != -1; i = sedge[i].next) {
        Edge e = sedge[i];
        if(e.v == fa || vis[e.v]) continue;
        num_dfs(e.v, u, step+1);
    }
}

int main(void)
{
	int n, m, cas = 0;
	while(scanf("%d%d", &n, &m) != EOF) {
        if(!n && !m) break;
        for(int i = 0; i <= n; ++i) {
            h[i] = num[i] = -1;
            dfn[i] = low[i] = 0;
        }
        edge_cnt = 0;
        int u, v;
        for(int i = 0; i < m; ++i) {
            scanf("%d%d", &u, &v);
            add(u, v);
        }
        scc = Top = indx = 0;
        dfs(1, -1);

        for(int i = 0; i <= scc; ++i) {
            sh[i] = -1; deep[i] = 0;
        }
        sedge_cnt = 0;
        for(int i = 1; i <= n; ++i) {
            for(int j = h[i]; j != -1; j = edge[j].next) {
                Edge e = edge[j];
                if(num[i] == num[e.v]) continue;
                sadd(num[i], num[e.v]);
            }
        }

        for(int i = 1; i <= scc; ++i) vis[i] = false;
        num_dfs(1, -1, 1);

        int Q, ans = scc-1;
        scanf("%d", &Q);
        printf("Case %d:\n", ++cas);

        for(int i = 1; i <= scc; ++i) vis[i] = false;
        while(Q--) {
            scanf("%d%d", &u, &v);
            int cnt = 0;
            u = num[u]; v = num[v];
            while(deep[u] > deep[v]) {
                if(!vis[u]) {
                    vis[u] = true; cnt++;
                }
                u = pre[u];
            }
            while(deep[u] < deep[v]) {
                if(!vis[v]) {
                    vis[v] = true; cnt++;
                }
                v = pre[v];
            }
            while(u != v) {
                if(!vis[u]) {
                    vis[u] = true; cnt++;
                }
                if(!vis[v]) {
                    vis[v] = true; cnt++;
                }
                u = pre[u]; v = pre[v];
            }
            ans -= cnt;
            printf("%d\n", ans);
        }
        puts("");
	}
	return 0;
}