题意:
给定n个点m条边的无向图
Q个询问:
问加上这条边后图中还有多少桥。
注意询问不是独立的(加了边在后面都有效)
思路:
先缩点得到缩点树,加上一条边后[u, LCA(u,v), v] 成环,则删掉这里的点,并把集合向上合并
#include <stdio.h> #include <string.h> #include <iostream> #include <math.h> #include <queue> #include <set> #include <algorithm> #include <stdlib.h> #include <vector> #define inf 107374182 #define ll int using namespace std; #define N 100100 //N为点 #define M 200100 struct Edge{ int from, to, nex; bool cut; }edge[M*2]; int head[N], edgenum; void addedge(int u, int v){ Edge E={u,v,head[u],false}; edge[ edgenum ] = E; head[u] = edgenum++; } int n, m; int dfn[N], low[N], tarjan_time, tar, Stack[N*5], top; int Belong[N]; bool iscut[N]; void tarjan(int u, int fa){ dfn[u] = low[u] = ++tarjan_time; Stack[++top] = u; int child = 0, flag = 1; for(int i = head[u]; ~i; i = edge[i].nex) { int v = edge[i].to; if(flag && v==fa){flag = 0; continue;} if(!dfn[v]) { child++; tarjan(v, u); low[u] = min(low[u], low[v]); if(low[v] >= dfn[u]) { iscut[u] = true; if(low[v]>dfn[u]) edge[i].cut = edge[i^1].cut = true; } } else low[u] = min(low[u], dfn[v]); } if(child == 1 && fa<0)iscut[u] = false; if(low[u] == dfn[u]) { tar++; do { Belong[ Stack[top] ] = tar; }while(Stack[top--] != u); } } void init(){ memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(iscut, 0, sizeof(iscut)); memset(Belong, -1, sizeof(Belong)); tarjan_time = 0; top = 0; tar = 0; tarjan(1,1); } vector<int>G[N]; const int MAXN = 101000; int rmq[2*MAXN];//rmq数组,就是欧拉序列对应的深度序列 vector<int>son[N]; int Father[N]; struct ST { int mm[2*MAXN]; int dp[2*MAXN][20];//最小值对应的下标 void init(int n) { mm[0] = -1; for(int i = 1;i <= n;i++) { mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1]; dp[i][0] = i; } for(int j = 1; j <= mm[n];j++) for(int i = 1; i + (1<<j) - 1 <= n; i++) dp[i][j] = rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1]; } int query(int a,int b)//查询[a,b]之间最小值的下标 { if(a > b)swap(a,b); int k = mm[b-a+1]; return rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k]; } }; int F[MAXN*2];//欧拉序列,就是dfs遍历的顺序,长度为2*n-1,下标从1开始 int P[MAXN];//P[i]表示点i在F中第一次出现的位置 int cnt; ST st; int deep[N]; void dfs(int u,int pre,int dep) { F[++cnt] = u; rmq[cnt] = dep; P[u] = cnt; for(int i = 0;i <G[u].size(); i++) { int v = G[u][i]; if(v == pre)continue; dfs(v,u,dep+1); F[++cnt] = u; rmq[cnt] = dep; son[u].push_back(v); Father[v] = u; deep[v] = deep[u]+1; } } void LCA_init(int root,int node_num)//查询LCA前的初始化 { cnt = 0; dfs(root,root,0); st.init(2*node_num-1); } int query_lca(int u,int v)//查询u,v的lca编号 { return F[st.query(P[u],P[v])]; } int f[N]; int find(int x){return x==f[x]?x:f[x] = find(f[x]);} void Union(int x, int y){ int fx = find(x), fy = find(y); if(fx==fy)return ; if(deep[fx]<deep[fy])swap(fx,fy); f[fx] = fy; } int main(){ int i, j, u, v, Cas = 1, Q; while(scanf("%d %d",&n,&m),n+m){ printf("Case %d:\n", Cas++); for(i=0;i<=n;i++)f[i]=i; memset(head, -1, sizeof(head)), edgenum = 0; while(m--) { scanf("%d %d",&u,&v); addedge(u,v); addedge(v,u); } init(); for(i=0;i<=tar;i++)G[i].clear(); for(i=0;i<edgenum;i++){ u = Belong[edge[i].from], v = Belong[edge[i].to]; if(u!=v)G[u].push_back(v); } for(i = 0;i<=tar;i++)son[i].clear(); Father[1] = 1; deep[1] = 0; LCA_init(1,tar); int now = tar-1; scanf("%d",&Q); while(Q--) { scanf("%d %d",&u,&v);if(now==0){puts("0");continue;} u = Belong[u], v =Belong[v]; int fa = query_lca(u, v); fa = find(fa); u = find(u); v = find(v); while(u!=fa){ now--; f[u] = fa; u = Father[u]; u = find(u); } while(v!=fa){ now--; f[v] = fa; v = Father[v]; v = find(v); } printf("%d\n", now); } } return 0; }