biconnected component (bcc)
Concept:
There are two kinds of bi-connected components, one is bi-connected component and the other is edge bi-connected component. If the removal of any node (an edge) in an undirected graph does not change the connectivity of the graph, that is, there is no cut point (bridge), it is called a point (edge) connected graph.
Each maximal point (edge) connected subgraph of an undirected graph is called the point (edge) connected component of the undirected graph. Tarjan algorithm can be used to find the double connected components. Baidu Encyclopedia
Tip: After learning the tarjan algorithm and the algorithm of cutting edge, it will be better to understand.
Point-to-point and edge-to-edge connectivity
- Connectivity: In undirected graphs, all points can reach each other
- Connected Components: Interconnected Subgraphs
- Point Double Connectivity: After deleting a point, the graph is still connected
- Edge Double Connectivity: After deleting an edge, the graph is still connected
Summary
In an undirected graph, if there are at least two "non-repetitive" paths between any two points, it is said that the graph is point-doubly connected (biconnected for short).
In an undirected graph, the maximal subgraph of point-doubly connected is called point-doubly connected component (BCC).
Nature
- There are at least two non-repetitive paths between any two points, which are equivalent to deleting any point in the graph and will not change the connectivity of the graph, that is, no cut point in BCC.
- If there is a common point between BCC s, then the common point is the cut point of the original graph.
- Cut points in undirected connected graphs must belong to at least two BCCs, and non-cut points belong to only one BCC.
algorithm
Maintain a stack in the process of Tarjan. Each time Tarjan arrives at a node, the node is put on the stack. In backtracking, if the low value of the target node is not less than the dfn value of the current node, the stack node and the current node are stored in BCC.
To be honest, I don't think saving points is more difficult to understand and realize than saving points. The following will explain)
Understand
First of all, in the BCC data I found, the graph composed of two points and one edge is called BCC in the implementation of the algorithm, which is also used in this article.
As follows:
I guess it's probably because of the definition of cut point. Neither of the two points in this graph is cut point. So this graph belongs to BCC?
In a word, attention should be paid to the requirements of the problem surface when doing the problem. If the BCC is not included in the requirement, the size of each BCC can be judged.
Cut points in undirected connected graphs must belong to at least two BCCs, and non-cut points belong to only one BCC.
With the above provisions, we can understand this: cut points even adjacent will belong to at least two BCC; BCC intersection points are cut points, so non-cut points belong to only one BCC.
To a node, put the node on the stack
Why stack storage? Because DFS is from top to bottom, while separated BCC is from bottom to top, which requires a data structure of "last in, first out" - stack.
When backtracking, if the low value of the target node is not less than the dfn value of the current node, it goes out of the stack until the target node (the target node is out of the stack), and stores the stack node and the current node in BCC.
For each BCC, the first point found in the DFS tree must be the cut point or the root of the DFS tree.
It is proved that the cut point is the intersection point between BCCs, so the cut point is on the edge of BCC, and the BCC is connected by the cut point, so the first point that BCC is found in DFS tree is the cut point; in particular, for the BCC that the point that begins DFS belongs to, the first point that BCC is found is the tree root of DFS tree.
The above conclusion is equivalent to that each BCC is in the subtree of its first discovered point (a cut point or tree root).
In this way, every time a BCC (low [v]>= DFN [u]) is found, the subtree is put out of the stack, and the subtree and the current node (cut point or root) are added to the BCC. The above operation is equivalent to this description.
(I just tuned poj2942 overnight because I didn't understand the condition of "putting the subtree out of the stack".)
To sum up, is the point of deposit well understood? Although edge saving does not involve duplication (cut points belong to at least two BCC s), there are many useless operations. Personally, I think deposit is also a good choice.
Template
1 #include<cstdio>
2 #include<cctype>
3 #include<vector>
4 using namespace std;
5 struct edge
6 {
7 int to,pre;
8 }edges[1000001];
9 int head[1000001],dfn[1000001],dfs_clock,tot;
10 int num;//Quantity of BCC
11 int stack[1000001],top;//Stack
12 vector<int>bcc[1000001];
13 int tarjan(int u,int fa)
14 {
15 int lowu=dfn[u]=++dfs_clock;
16 for(int i=head[u];i;i=edges[i].pre)
17 if(!dfn[edges[i].to])
18 {
19 stack[++top]=edges[i].to;//Searched Points on the Stack
20 int lowv=tarjan(edges[i].to,u);
21 lowu=min(lowu,lowv);
22 if(lowv>=dfn[u])//It's a cut point or root.
23 {
24 num++;
25 while(stack[top]!=edges[i].to)//Put the point out of the stack until the target point
26 bcc[num].push_back(stack[top--]);
27 bcc[num].push_back(stack[top--]);//Target point out of stack
28 bcc[num].push_back(u);//Don't forget to save the current point in bcc
29 }
30 }
31 else if(edges[i].to!=fa)
32 lowu=min(lowu,dfn[edges[i].to]);
33 return lowu;
34 }
35 void add(int x,int y)//Adjacent table edge saving
36 {
37 edges[++tot].to=y;
38 edges[tot].pre=head[x];
39 head[x]=tot;
40 }
41 int main()
42 {
43 int n,m;
44 scanf("%d%d",&n,&m);
45 for(int i=1;i<=m;i++)
46 {
47 int x,y;
48 scanf("%d%d",&x,&y);
49 add(x,y),add(y,x);
50 }
51 for(int i=1;i<=n;i++)//Traversing n points tarjan
52 if(!dfn[i])
53 {
54 stack[top=1]=i;
55 tarjan(i,i);
56 }
57 for(int i=1;i<=num;i++)
58 {
59 printf("BCC#%d: ",i);
60 for(int j=0;j<bcc[i].size();j++)
61 printf("%d ",bcc[i][j]);
62 printf("\n");
63 }
64 return 0;
65 }
form:https://www.cnblogs.com/LiHaozhe/p/9527136.html
[Double Connected Components]
1. Definition of Edge Double Connected Components
Any two points in the component can always find two paths with different sides to reach each other. All in all, it's a circle. You can reach each other in both directions, at least one point.
2. Definition of Point Double Connected Components
Referring to the above, the only difference is that any two points can find a different path to each other. It's also a circle, either positive or negative, at least one point.
3. Notices about the template code of edge and point connected components
Edge-doubly connected components:
1. All the sons of each node traverse before calculating the component size. Please distinguish it from the point double-connected.
2. Cutting the top can only belong to one component, please distinguish it from cutting edge; (easy to confuse)
3. Pay attention to whether j is the parent of i.
The above points are as follows:
1 void DFS(int i,int fd)//fd It's the father's side. 2 { 3 low[i]=dfn[i]=++dfs_clock; 4 vis[i]=1; 5 stk[++top]=i;//Stack node 6 for(int p=last[i];p;p=E[p].pre) 7 { 8 int j=E[p].to,id=E[p].id; 9 if(vis[j]) 10 { 11 if(dfn[j]<dfn[i]&&fd!=id) low[i]=min(low[i],dfn[j]); 12 continue; 13 } 14 DFS1(j,id); 15 low[i]=min(low[i],low[j]); 16 } 17 18 //All the sons went through the journey and asked again. 19 if(low[i]==dfn[i]) 20 { 21 cc++; 22 int x; 23 while(1) 24 { 25 x=stk[top--]; 26 belong[x]=cc; 27 size[cc]++; 28 if(x==i) break;//Attention is equal to i Just jump out, that is i Can only belong to one edge connected component 29 } 30 maxcc=max(maxcc,size[cc]); 31 } 32 }
Point Double Connected Components:
1. Every time a son is traversed, the point-connected component is calculated.
2. Cutting roof can belong to many connected components. Please distinguish it from cutting edge.
3. When i is the root node, it requires at least two sons to be the cut point.
The above points are as follows:
1 void DFS(int i,int fd)//fd It's the father's side. 2 { 3 low[i]=dfn[i]=++dfs_clock; 4 stk[++top]=i;//Stack node 5 int chd=0;//Number of sons 6 7 for(int p=last[i];p;p=E[p].pre) 8 { 9 10 int j=E[p].to,id=E[p].id; 11 if(dfn[j]) 12 { 13 if(dfn[j]<dfn[i]&&id!=fd) low[i]=min(low[i],dfn[j]); 14 continue; 15 } 16 17 18 chd++; 19 DFS(j,id); 20 low[i]=min(low[i],low[j]); 21 22 23 if(low[j]>=dfn[i])//After traversing a son, see if there are connected components 24 { 25 cut[i]=1;//Preliminary Judgment i It's roof cutting (not necessarily, depending on the final conditions) 26 bcc_cnt++; 27 bcc[bcc_cnt].push_back(i);//Just put i Save in, but not in. i Which component does it belong to, because i It's cutting the top, maybe it belongs to other components. 28 int x; 29 while(1) 30 { 31 x=stk[top--]; 32 bcc[bcc_cnt].push_back(x); 33 if(x==j) break;//Be aware j End 34 } 35 } 36 37 } 38 39 40 if(fd==0&&chd==1) cut[i]=0;//This conclusion should be known to all. 41 }
[Strongly Connected Components]
Definition
The rings on a digraph are not verbose, and are similar to the two above, at least one point.
2. Notices about template code
1. All the sons at each point traverse before they begin to calculate the components; (Similar to edge-connected components)
2. Each point can only belong to one component.
1 void DFS(int i) 2 { 3 low[i]=dfn[i]=++dfs_clock; 4 stk[++top]=i; 5 for(int p=last[i];p;p=E[p].pre) 6 { 7 int j=E[p].v; 8 if(dfn[j]) 9 { 10 if(!belong[j]) low[i]=min(low[i],dfn[j]); 11 continue; 12 } 13 14 DFS(j); 15 low[i]=min(low[i],low[j]); 16 } 17 18 if(dfn[i]==low[i]) 19 { 20 scc++; 21 while(1) 22 { 23 int x=stk[top--]; 24 belong[x]=scc; 25 size[scc]++; 26 if(x==i) break; 27 } 28 } 29 }
[Common Models and Questions for Strongly Connected Components and Double Connected Components] (Limited Level, will be updated as soon as possible)
Double Connected Components
1. The given graph is a disconnected graph, such as:
A. There are some points, some edges, and the least edges, so that the whole graph becomes a connected graph.
Approximate method: Find out all components, regard each component as a point, count the degree of each point, one degree is a CNT plus 1, the answer is (cnt+1) / 2;
b. There are some points, some edges, and ask at least how many points are singled out.
Rough method: Find out all the components, but pay attention to different problems may have special requirements (such as the round table Knight requires odd circles, to use the dichotomy decision)
c. Variations
2. The given graph is a connected graph, such as:
A. Given a starting point and an end point, we can find out whether various problems can be realized.
Approximate method: Find out all components and take each component as a point, so the problem is simplified.
b. Give a graph and then have a lot of offline answers.
Rough method: Find out all the components, and then get the information of the upper and lower subtrees.
c. Variations;
Strongly Connected Components
1. Given are disconnected graphs, such as:
a. Some points, some directed edges, find at least how many edges to add so that any two points can reach each other.
Approximate method: Find out all components, shrinkage points, respectively, the number of points with a degree of entry of 0, take the most as the answer;
b. There are some points, some directed edges, and find out how many points a road can pass through on this map at most.
Approximate method: Find out all components, reduce points, form one or more DAG graphs, and then do dp on DAG.
c. Some points, some directed edges, give some special points, find the end point is the longest path of the special point.
Approximate method: Find out all components, mark which components have special points, and then the dp of DAG.
2. Connected graphs are given, which are relatively few and simple.
Summary:
1. When encountering disconnected graphs, it is almost certain that connected components, whether undirected or directed, are required; (It saves a lot of thinking time)
2. All operations on edges and points have the same effect on any point in the same component, and almost all operations on rough points solve the problem of contraction points.
3. Special circumstances must be considered, such as that the whole graph is a connected component, etc. (10-20 points are taken into account);
4. For the bi-connected component, the edge or point bi-connected component should be analyzed.
5. To get the title, we should first find out whether the connected graph or the disconnected graph is given.
Original: https://blog.csdn.net/WWWengine/article/details/80616779
POJ3694 Network
https://vjudge.net/problem/POJ-3694
problem
A network administrator manages a large network. The network consists of N computers and M links between pairs of computers. Any pair of computers are connected directly or indirectly by successive links, so data can be transformed between any two computers. The administrator finds that some links are vital to the network, because failure of any one of them can cause that data can't be transformed between some computers. He call such a link a bridge. He is planning to add some new links one by one to eliminate all bridges.
You are to help the administrator by reporting the number of bridges in the network after each new link is added.
Input
The input consists of multiple test cases. Each test case starts with a line containing two integers N(1 ≤ N ≤ 100,000) and M(N - 1 ≤ M ≤ 200,000).
Each of the following M lines contains two integers A and B ( 1≤ A ≠ B ≤ N), which indicates a link between computer A and B. Computers are numbered from 1 to N. It is guaranteed that any two computers are connected in the initial network.
The next line contains a single integer Q ( 1 ≤ Q ≤ 1,000), which is the number of new links the administrator plans to add to the network one by one.
The i-th line of the following Q lines contains two integer A and B (1 ≤ A ≠ B ≤ N), which is the i-th added new link connecting computer A and B.
The last test case is followed by a line containing two zeros.
Output
For each test case, print a line containing the test case number( beginning with 1) and Q lines, the i-th of which contains a integer indicating the number of bridges in the network after the first i new links are added. Print a blank line after the output for each test case.
Sample Input
3 2 1 2 2 3 2 1 2 1 3 4 4 1 2 2 1 2 3 1 4 2 1 2 3 4 0 0
Sample Output
Case 1: 1 0 Case 2: 2 0
Give you an undirected graph with N points and M edges, and have Q times of edge addition. Ask the number of bridges in the graph after each edge addition.
Obviously, if the two ends of the joined edge are in the same side, the number of bridges will not change. So we first use Tarjan to reduce edge-doubly connected components of the original graph to get a tree.
Next, when two endpoints are not on one side, it is clear that the reduction in the number of bridges is equal to the tree distance between the two endpoints. We find the distance on the tree and mark the edges between the two endpoints, that is, the edge length is changed from 1 to 0. Every time you find the distance on the tree, you climb up one by one, and you can mark the edges by the way. Time complexity is O(M+QN), which can be passed through this topic, but obviously not good.
Since the edge length becomes zero, we don't need to deal with these edges anymore, so we can use the method of tree reduction to merge the points between two endpoints into a set, and then jump directly to the LCA position next time we climb to these two endpoints.
Solutions:
1. Tarjan algorithm is used to calculate the two-way components of each side and record which component each point belongs to.
2. Reduce the two-way components of each side into a point, and finally get a tree. And what if we want to get a tree with roots? In fact, when Tarjan algorithm is implemented, a rooted tree has been formed. So we just need to record the parent node and depth of each point on the basis of Tarjan algorithm.
3. When asked each time, if two points are in the same component, their connection will not reduce the number of bridges. If two points are in different components, then the bridge on the path of u-> LCA (u, v) and v-> LCA (u, v) can be reduced, and the points on the path can be reduced to one point, i.e. merged into one component.
For the treatment of shrinkage points:
Method 1: For a component, one point can be set as a real point and the rest as a virtual point. The real point represents all the information of this component. Although the virtual point belongs to the point of this component, it turns a blind eye to him. What we have to do is to select a point in this component to represent the whole component.
Method 2: Similarly, we need to select a representative for each component to represent that component. Different from the "blind" of method 1, method 2 sets up a set of attributions for each point, that is to say, which set this point belongs to. Because in the process of processing, one set may be included by another set, so we can use the path compression to find the final set of a point quickly.
Method 1:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include <vector> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 #define ms(a,b) memset((a),(b),sizeof((a))) 13 using namespace std; 14 typedef long long LL; 15 const double EPS = 1e-8; 16 const int INF = 2e9; 17 const LL LNF = 2e18; 18 const int MAXN = 1e5+10; 19 20 struct Edge 21 { 22 int to, next; 23 }edge[MAXN*8]; 24 int tot, head[MAXN]; 25 26 int index, dfn[MAXN], low[MAXN]; 27 int isbridge[MAXN], sum_bridge; 28 int fa[MAXN], depth[MAXN]; 29 30 void addedge(int u, int v) 31 { 32 edge[tot].to = v; 33 edge[tot].next = head[u]; 34 head[u] = tot++; 35 } 36 37 void Tarjan(int u, int pre) 38 { 39 dfn[u] = low[u] = ++index; 40 depth[u] = depth[pre] + 1; //Recording depth 41 fa[u] = pre; //Recording Father's Node 42 for(int i = head[u]; i!=-1; i = edge[i].next) 43 { 44 int v = edge[i].to; 45 if(v==pre) continue; 46 if(!dfn[v]) 47 { 48 Tarjan(v, u); 49 low[u] = min(low[u], low[v]); 50 if(low[v]>dfn[u]) //isbridge[v]Represented in a tree, with v Is the edge of the node for son a bridge? 51 isbridge[v] = 1, sum_bridge++; 52 } 53 else 54 low[u] = min(low[u], dfn[v]); 55 } 56 } 57 58 void LCA(int u, int v) 59 { 60 if(depth[u]<depth[v]) swap(u, v); 61 while(depth[u]>depth[v]) //Go up deep first. When you meet a bridge, delete it. 62 { 63 if(isbridge[u]) sum_bridge--, isbridge[u] = 0; 64 u = fa[u]; 65 } 66 while(u!=v) //When the depth is the same, climb together. When you meet a bridge, delete it. 67 { 68 if(isbridge[u]) sum_bridge--, isbridge[u] = 0; 69 u = fa[u]; 70 if(isbridge[v]) sum_bridge--, isbridge[v] = 0; 71 v = fa[v]; 72 } 73 } 74 75 void init() 76 { 77 tot = 0; 78 memset(head, -1, sizeof(head)); 79 80 index = 0; 81 memset(dfn, 0, sizeof(dfn)); 82 memset(low, 0, sizeof(low)); 83 memset(isbridge, 0, sizeof(isbridge)); 84 85 sum_bridge = 0; 86 } 87 88 int main() 89 { 90 int n, m, kase = 0; 91 while(scanf("%d%d", &n, &m) && (n||m) ) 92 { 93 init(); 94 for(int i = 1; i<=m; i++) 95 { 96 int u, v; 97 scanf("%d%d", &u, &v); 98 addedge(u, v); 99 addedge(v, u); 100 } 101 102 depth[1] = 0; 103 Tarjan(1, 1); 104 int q, a, b; 105 scanf("%d", &q); 106 printf("Case %d:\n", ++kase); 107 while(q--) 108 { 109 scanf("%d%d", &a, &b); 110 LCA(a, b); 111 printf("%d\n", sum_bridge); 112 } 113 printf("\n"); 114 } 115 }
Method 2:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include <vector> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 #define ms(a,b) memset((a),(b),sizeof((a))) 13 using namespace std; 14 typedef long long LL; 15 const double EPS = 1e-8; 16 const int INF = 2e9; 17 const LL LNF = 2e18; 18 const int MAXN = 1e6+10; 19 20 struct Edge 21 { 22 int to, next; 23 }edge[MAXN], edge0[MAXN]; //edge For the initial graph, edge0 For Reconstruction Map 24 int tot, head[MAXN], tot0, head0[MAXN]; 25 26 int index, dfn[MAXN], low[MAXN]; 27 int top, Stack[MAXN], instack[MAXN]; 28 int belong[MAXN]; 29 int fa[MAXN], depth[MAXN]; //fa The parent node of the current node is recorded when reconstructing the graph. depth Record the depth of the current node 30 int sum_bridge; 31 32 //find x Ultimate affiliation 33 int find(int x) { return belong[x]==x?x:belong[x]=find(belong[x]); } 34 35 void addedge(int u, int v, Edge edge[], int head[], int &tot) 36 { 37 edge[tot].to = v; 38 edge[tot].next = head[u]; 39 head[u] = tot++; 40 } 41 42 void Tarjan(int u, int pre) 43 { 44 dfn[u] = low[u] = ++index; 45 Stack[top++] = u; 46 instack[u] = true; 47 for(int i = head[u]; i!=-1; i = edge[i].next) 48 { 49 int v = edge[i].to; 50 if(v==pre) continue; 51 if(!dfn[v]) 52 { 53 Tarjan(v, u); 54 low[u] = min(low[u], low[v]); 55 if(low[v]>dfn[u]) sum_bridge++; 56 } 57 else if(instack[v]) 58 low[u] = min(low[u], dfn[v]); 59 } 60 61 if(dfn[u]==low[u]) 62 { 63 int v; 64 do 65 { 66 v = Stack[--top]; 67 instack[v] = false; 68 belong[v] = u; //Set the number of the set as the first point of the connected component 69 }while(v!=u); 70 } 71 } 72 73 void build(int u, int pre) 74 { 75 fa[u] = pre; //Record Father Node 76 depth[u] = depth[pre] + 1; //Recording depth 77 for(int i = head0[u]; i!=-1; i=edge0[i].next) 78 if(edge0[i].to!=pre) //Prevent going back 79 build(edge0[i].to, u); 80 } 81 82 83 int LCA(int u, int v) //Look left and right. LCA 84 { 85 if(u==v) return u; //Because there must be two nodes LCA, So there must be u==v When 86 87 //It's possible to climb several depths in a single step, because the middle node has shrunk upwards. 88 if(depth[u]<depth[v]) swap(u, v); //A deep climb 89 sum_bridge--; 90 int lca = LCA(find(fa[u]), v); 91 return belong[u] = lca; //Eureka LCA,Set the current node's collection to LCA The set of subordinates of 92 } 93 94 void init() 95 { 96 tot = tot0 = 0; 97 memset(head, -1, sizeof(head)); 98 memset(head0, -1, sizeof(head0)); 99 100 index = top = 0; 101 memset(dfn, 0, sizeof(dfn)); 102 memset(low, 0, sizeof(low)); 103 memset(instack, 0, sizeof(instack)); 104 105 sum_bridge = 0; 106 } 107 108 int main() 109 { 110 int n, m, kase = 0; 111 while(scanf("%d%d", &n, &m) && (n||m) ) 112 { 113 init(); 114 for(int i = 1; i<=m; i++) 115 { 116 int u, v; 117 scanf("%d%d", &u, &v); 118 addedge(u, v, edge, head, tot); 119 addedge(v, u, edge, head, tot); 120 } 121 122 Tarjan(1, 1); 123 for(int u = 1; u<=n; u++) //Reconstruction and Mapping 124 for(int i = head[u]; i!=-1; i = edge[i].next) 125 { 126 int tmpu = find(u); 127 int tmpv = find(edge[i].to); 128 if(tmpu!=tmpv) 129 addedge(tmpu, tmpv, edge0, head0, tot0); 130 } 131 132 depth[find(1)] = 0; 133 build(find(1), find(1)); //Turning Rootless Trees into Rooted Trees 134 135 int q, a, b; 136 scanf("%d", &q); 137 printf("Case %d:\n", ++kase); 138 while(q--) 139 { 140 scanf("%d%d", &a, &b); 141 LCA(find(a), find(b)); 142 printf("%d\n", sum_bridge); 143 } 144 printf("\n"); 145 } 146 }
Solution: tarjan+lca. For a bridge, fa[v] is used to denote the parent node of v, and br[v] is used to denote whether it is a bridge from the parent node of V to the side of v, and 1 is used to denote whether it is a bridge or not. After obtaining fa [] and br [], if the edges of u and V are added, a ring will be formed between the nearest common ancestors of u and V and between u and V. The edge of the bridge in this ring will no longer be a bridge, so it is enough to record the number of bridges.
1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 using namespace std; 14 15 typedef long long LL; 16 #define mem(a, n) memset(a, n, sizeof(a)) 17 #define rep(i, n) for(int i = 0; i < (n); i ++) 18 #define REP(i, t, n) for(int i = (t); i < (n); i ++) 19 #define FOR(i, t, n) for(int i = (t); i <= (n); i ++) 20 #define ALL(v) v.begin(), v.end() 21 #define si(a) scanf("%d", &a) 22 #define sii(a, b) scanf("%d%d", &a, &b) 23 #define siii(a, b, c) scanf("%d%d%d", &a, &b, &c) 24 #define pb push_back 25 const int inf = 0x3f3f3f3f, N = 1e5 + 5, MOD = 1e9 + 7; 26 27 int T, cas = 0; 28 int n, m, q; 29 int dfn[N], low[N], fa[N], head[N]; 30 bool brige[N], vis[N]; 31 struct edge { 32 int v, next; 33 }e[4 * N]; 34 int brigeNum = 0, ne = 0, dfsNum = 0; 35 void addEdge(int u, int v) { 36 e[ne].v = v; 37 e[ne].next = head[u]; 38 head[u] = ne ++; 39 } 40 // Imp 41 void init() { 42 mem(dfn, 0); 43 mem(low, 0); 44 mem(fa, -1); 45 mem(vis, 0); 46 mem(brige, 0); 47 mem(head, -1); 48 dfsNum = ne = brigeNum = 0; 49 } 50 51 void Tarjan(int u, int f) { 52 dfn[u] = low[u] = ++ dfsNum; 53 for(int i = head[u]; i != -1; i = e[i].next) { 54 int v = e[i].v; 55 if(!dfn[v]) { 56 vis[v] = 1; 57 fa[v] = u; 58 Tarjan(v, u); 59 low[u] = min(low[u], low[v]); 60 if(dfn[u] < low[v]) { 61 brigeNum ++; 62 brige[v] = 1; 63 } 64 } else if(v != f) low[u] = min(low[u], dfn[v]); 65 } 66 } 67 68 void work(int u) { 69 if(brige[u]) brigeNum --, brige[u] = 0; 70 } 71 72 void LCA(int u, int v) { 73 if(dfn[u] > dfn[v]) swap(u, v); 74 while(dfn[u] < dfn[v]) { 75 work(v); 76 v = fa[v]; 77 } 78 while(v != u) { 79 work(u); 80 work(v); 81 u = fa[u], v = fa[v]; 82 } 83 } 84 85 int main(){ 86 #ifdef LOCAL 87 freopen("/Users/apple/input.txt", "r", stdin); 88 // freopen("/Users/apple/out.txt", "w", stdout); 89 #endif 90 91 while(sii(n, m) != EOF, n + m) { 92 init(); 93 int u, v; 94 rep(i, m) { 95 sii(u, v); 96 addEdge(u, v), addEdge(v, u); 97 } 98 FOR(i, 1, n + 1) fa[i] = i; 99 Tarjan(1, -1); 100 printf("Case %d:\n", ++ cas); 101 si(q); 102 rep(i, q) { 103 sii(u, v); 104 LCA(u, v); 105 printf("%d\n", brigeNum); 106 } puts(""); 107 108 } 109 110 return 0; 111 }
Topic: Initialization of an undirected graph with n points with m edges
Then q operations represent the number of bridges left in each q operation by building an edge between point a and point b.
Initialization can be calculated by tarjan algorithm for two points that are not cut edges, which can be regarded as a set so that the set can be retracted by using and searching the set.
The final result is that the edge of a tree is the edge < u, v > of all the bridge q queries in the graph. If u and v indicate the new edge in a set, it will not affect the result.
If the edges of u and v between two sets are added < u, v > then the bridge property will be lost so that the set between two sets can be traversed by LCA. After adding < u, v > to the edge, the set between the two sets becomes a ring, that is, the set can be reduced to a point in the process of merging the set. The disappeared bridge can be subtracted from the sum.
I've been wondering why I use LCA to solve this problem. It turns out that they will form a tree after the shrinkage point. Then because they have already passed the shrinkage point, the edges of these trees are all bridges (finally understand why they say that the edge of the tree after the shrinkage point is a bridge). So if the edge is a shrinkage point, then it belongs to a shrinkage point.( The point inside the indentation point is a set) then it has no effect on the bridge in the original picture, but if the added edge belongs to two indentation points, then a ring will be formed. If the edge inside the ring is deleted arbitrarily, the tree will be interoperable. ORZ finally understands, then we can use the characteristics of LCA to figure out how many bridges have been reduced, because it is the most recent common ancestor, so the two new points on this side must be the shortest path (the smallest edge on the tree) through LCA, then we can get the result. The total number of bridges is subtracted from LCA. The top side is the answer to the question!!!!
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <stack> 5 using namespace std; 6 #define N 100010 7 #define M 400010 8 9 struct edge{ 10 int v; 11 int next; 12 }Edge[M];//Set of Edges 13 14 int node[N];//Vertex set 15 int DFN[N];//node u Search Number(time stamp) 16 int LOW[N];//u or u The sequence number of the earliest node in the stack traceable to the subtree(time stamp) 17 int fa[N];//Last node 18 int pre[N];//Finding Father Node 19 int n,m;//n: Number of points; m: Number of edges 20 int cnt_edge;//Side counter 21 int Index;//Serial number(time stamp) 22 int ans;//Number of bridges 23 24 25 void init()//Initialization,Be careful not to put n Initially 0 26 { 27 cnt_edge=0; 28 Index=0; 29 ans=0; 30 memset(Edge,0,sizeof(Edge)); 31 memset(node,-1,sizeof(node)); 32 memset(DFN,0,sizeof(DFN)); 33 memset(LOW,0,sizeof(LOW)); 34 memset(fa,0,sizeof(fa)); 35 memset(pre,0,sizeof(pre)); 36 for(int i=1;i<=n;i++) 37 { 38 pre[i]=i; 39 } 40 } 41 42 int Find(int x) 43 { 44 // while(n!=pre[n])//Writing like this will make mistakes. 45 // { 46 // n=pre[n]; 47 // } 48 // return n; 49 return pre[x] == x? pre[x]: (pre[x] = Find(pre[x])); 50 } 51 52 int Union(int u,int v) 53 { 54 int uu,vv; 55 uu=Find(u); 56 vv=Find(v); 57 if(vv==uu) 58 return 0; 59 pre[uu]=vv; 60 return 1; 61 } 62 63 void add_edge(int u,int v)//Adjacent table storage 64 { 65 Edge[cnt_edge].next=node[u]; 66 Edge[cnt_edge].v=v; 67 node[u]=cnt_edge++; 68 } 69 70 void tarjan(int u) 71 { 72 DFN[u]=LOW[u]=Index++; 73 for(int i=node[u];i!=-1;i=Edge[i].next) 74 { 75 int v=Edge[i].v; 76 if(v==fa[u]) //This is to be written before. 77 continue; 78 if(!DFN[v])//If point v Not visited 79 { 80 fa[v]=u; 81 tarjan(v); 82 LOW[u]=min(LOW[u],LOW[v]); 83 if(LOW[v]>DFN[u]) 84 { 85 ans++; 86 } 87 else Union(v,u); 88 } 89 else //if(v!=fa[u]) //If point v Has been visited 90 LOW[u]=min(LOW[u],DFN[v]); 91 } 92 } 93 94 void LCA(int u,int v) 95 { 96 if(DFN[v]<DFN[u]) 97 swap(u,v); 98 while(DFN[v]>DFN[u]) 99 { 100 if(Union(v,fa[v])) 101 ans--; 102 v=fa[v]; 103 } 104 while(v!=u) 105 { 106 if(Union(u,fa[u])) 107 ans--; 108 u=fa[u]; 109 } 110 } 111 112 int main() 113 { 114 //freopen("sample.txt","r",stdin); 115 int tot=0; 116 while(~scanf("%d %d",&n,&m)&&(m+n)) 117 { 118 init(); 119 while(m--) 120 { 121 int u,v; 122 scanf("%d %d",&u,&v); 123 add_edge(u,v); 124 add_edge(v,u); 125 } 126 fa[1]=1; 127 for(int i=1;i<=n;i++) 128 { 129 if(!DFN[i]) 130 { 131 tarjan(i); 132 } 133 } 134 int q; 135 scanf("%d",&q); 136 printf("Case %d:\n",++tot); 137 while(q--) 138 { 139 int u,v; 140 scanf("%d %d",&u,&v); 141 LCA(u,v); 142 printf("%d\n",ans); 143 144 } 145 printf("\n"); 146 } 147 return 0; 148 }