POJ 3694 Network(割边+LCA)

//Tarjan求桥 + LCA //蛋疼题目,确实想不到用LCA这个如此巧妙的方法,主要也就写下求桥,对Tarjan的DFN,LOW值再加深下理解 //重点是对桥的记录,本来想通过用边来记录,后来发现这是无解的,用桥与点对应就可以了 //一开始还想实践下LRJ书里面说的,缩点后形成的树每条边都是桥,后来觉得太难打了..就我这么渣的代码能力,打起来太蛋疼了- -||| #include #include #include #include using namespace std; const int MAXN = 100005,MAXM = 450005; int N,M,Q; int DFN[MAXN],LOW[MAXN],index,num; int head[MAXN],next[MAXM],V[MAXM],fa[MAXN],edge; bool bridge[MAXN]; int dep[MAXN],Case; void addEdge(int u,int v) { V[edge] = v; next[edge] = head[u]; head[u] = edge++; } //无向图求桥,对桥而言,都对应着一个后继结点,因此用后继结点记录桥即可 void dfs(int x,int p,int ee,int d) { fa[x] = p; dep[x] = d; DFN[x] = LOW[x] = ++index; for(int e = head[x];e != -1;e = next[e]) { int y = V[e]; if((e ^ 1) == ee) continue;//若为反向边则跳过,只记录父亲是不行的 if(!DFN[y]) { dfs(y,x,e,d+1); LOW[x] = min(LOW[x],LOW[y]); if(LOW[y] > DFN[x])//如果y能到达的深度最小的祖先比x的DFN值还小,证明y没有路径能够到通往x,故y对应着一条桥 { bridge[y] = 1; ++num; } } else LOW[x] = min(LOW[x],DFN[y]);//更新x所能通往的深度最小的祖先 } } void delBridge(int x) { if(bridge[x]) { bridge[x] = 0; num--; } } void LCA(int x,int y)//最朴素的LCA求法,记录每个结点的深度,和每个结点的父亲结点 { if(dep[x] < dep[y]) swap(x,y); while(dep[x] > dep[y])//先将两个点调节到深度一样的位置 { delBridge(x); x = fa[x]; } while(x != y)//一层层上升,并删除桥 { delBridge(x); x = fa[x]; delBridge(y); y = fa[y]; } } int main() { //freopen("in.txt","r",stdin); bool first = 1; while(scanf("%d%d",&N,&M)) { if(N == 0 && M == 0) break; first ? first = 0 : printf("/n"); int x,y; num = edge = 0; memset(head,-1,sizeof(head)); memset(bridge,0,sizeof(bridge)); memset(DFN,0,sizeof(DFN)); memset(dep,0,sizeof(dep)); memset(fa,0,sizeof(fa)); while(M--) { scanf("%d%d",&x,&y); addEdge(x,y); addEdge(y,x); } dfs(1,0,-1,1); printf("Case %d:/n",++Case); scanf("%d",&Q); while(Q--) { scanf("%d%d",&x,&y); addEdge(x,y); addEdge(y,x); LCA(x,y); printf("%d/n",num); } } } 

你可能感兴趣的:(network,图论)