Description:
给定一张n节点m条边的无向图,执行Q次操作,每次加一条无向边,询问当前桥的数量
思路:先双联通缩点,然后得到一棵树,对(x,y)连边的时候暴力LCA一下 然后将路径上的点用并查集全都压到LCA上,这样的话下次再搜到被压了的点就可以直接跳到LCA上了,向上压点的同时减去边的数量
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int N = 100050; const int M = 200050; int head[N],now; struct edges{ int to,next; }edge[M<<1]; void add(int u,int v){ edge[++now] = {v,head[u]}; head[u] = now;} int n,m,low[N],dfn[N],tot,cnt,fa[N],pre[N],dep[N],dict[N],ans; bool bridge[M<<1]; struct input{ int x,y; }inp[M]; void init(){ for(int i = 1; i <= n; i++) fa[i] = i; memset(inp,0,sizeof(inp)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); cnt = tot = 0; memset(pre,0,sizeof(pre)); memset(bridge,0,sizeof(bridge)); memset(head,0,sizeof(head)); memset(edge,0,sizeof(edge)); now = 1; memset(dep,0,sizeof(dep)); memset(dict,0,sizeof(dict)); } void tarjan(int x,int in_edge){ //边-双联通分量缩点 low[x] = dfn[x] = ++cnt; for(int i = head[x]; i; i = edge[i].next){ int v = edge[i].to; if(!dfn[v]){ tarjan(v,i); low[x] = min(low[x],low[v]); if(low[v] > dfn[x]) bridge[i] = bridge[i ^ 1] = 1; } else if(i != (in_edge ^ 1)) low[x] = min(low[x],dfn[v]); } return ; } void dfs(int x){ dict[x] = tot; for(int i = head[x]; i; i = edge[i].next){ int v = edge[i].to; if(dict[v] || bridge[i]) continue; dfs(v); } return ; } void new_dfs(int x){ for(int i = head[x]; i; i = edge[i].next){ int v = edge[i].to; if(pre[x] == v) continue; dep[v] = dep[x] + 1; pre[v] = x; new_dfs(v); } } int get(int x){ if(x != fa[x]) return fa[x] = get(fa[x]); return x; } int lca(int u,int v){ while(u != v){ if(dep[u] > dep[v]) u = pre[u]; else if(dep[u] < dep[v]) v = pre[v]; else{ u = pre[u]; v = pre[v]; } u = get(u),v = get(v); } return u; } int main(){ // freopen("data.out","r",stdin); int kase = 0, q; while(scanf("%d%d",&n,&m)!= EOF && n + m){ init(); int x,y; for(int i = 1; i <= m ; i++){ scanf("%d%d",&x,&y); inp[i] = {x,y}; add(x,y); add(y,x); } tarjan(1,0); for(int i = 1; i <= n; i++) if(!dict[i]) tot++, dfs(i); memset(head,0,sizeof(head)); memset(edge,0,sizeof(edge)); now = 0; for(int i = 1; i <= m; i++) //缩点后得到新图 if(dict[inp[i].x] != dict[inp[i].y]) add(dict[inp[i].x],dict[inp[i].y]),add(dict[inp[i].y],dict[inp[i].x]); new_dfs(1);//记录每个点的父节点 scanf("%d",&q); printf("Case %d:\n",++kase); ans = tot - 1; while(q--){ scanf("%d%d",&x,&y); x = get(dict[x]), y = get(dict[y]); int tmp = lca(x,y); while(x != tmp){ //将x的所有没有压到xy的LCA的点全都压到LCA上 ans--; fa[x] = tmp; x = get(pre[x]); } while(y != tmp){ //同上 ans--; fa[y] = tmp; y = get(pre[y]); } printf("%d\n",ans); } puts(""); } return 0; }