Bi-connected component (point-bi-connected component-edge-bi-connected component)

Keywords: PHP network less REST

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

  1. 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.
  2. If there is a common point between BCC s, then the common point is the cut point of the original graph.
  3. 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 }

 

 

 



Posted by kutatishh on Wed, 17 Jul 2019 14:50:36 -0700