B - Network
Time Limit:5000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u
Submit Status Practice POJ 3694
Description
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
题意 : 给一个连通图(有向);有Q个操作,每次操作加一条边,问每次加完边后图的割桥有多少条 ?
我们先求出整个图的联通分量和桥; 这样整个图就可以简化为 bridge个桥 把 sun个联通分量 连起来, 特别是把每个联通分量看成一个点,给上序号; 图就更简便了;
点是连通分量,边是桥; 更直观的这个图就相当与一颗树 ; 当我们加入一条边后,如果两个点属于一个联通分量,则相当于没添加,因为对图不影响,
因为把连通分量看做点了 ,如果两点所在的连通分量不通,则在这颗树上添加了一条边; 并且这颗树就会出现一个环了 ; 那么树上属于这个环内的边就不再是桥了,
为了好处理,我们把树分层次(高度);树根在0层 ;
然后当加了边有两种情况 ;
比如 这颗树,u,v 两个点连边; (u点的在树上的高度比v高)
1. 当从v开始向上通过父亲节点查找(v=father[v]),也就是u是v的祖先时,那么只要一直向上找,边找边把边的数量减去(桥减1);直到 v=u ;
2. 当u不是v的祖先时,则可以先从v向上找,知道v和u在同一高度(dep[u]==dep[v]);接着需要u,v都向上找,直到 u==v ;
#include<cstdio> #include<cstring> #include<map> #include<vector> #include<cmath> #include<cstdlib> #include<stack> #include<queue> #include <iomanip> #include<iostream> #include<algorithm> using namespace std ; const int N=100100 ; const int M=N*4; const int inf=1<<30 ; struct node { int to ,next ; bool ok ; }edge[M]; int head[N],low[N],dnf[N],Stack[N],belong[N]; bool Instack[N]; int top,dfsn,sum,bridge; int father[N],dep[N],a[N]; vector<int>g[N]; void add(int u ,int v) { edge[top].to=v;edge[top].next=head[u];edge[top].ok=false ; head[u]=top++; } void Tarjan(int u, int pre) { int v ; low[u]=dnf[u]=++dfsn ; Stack[top++]=u; Instack[u]=true ; for(int i = head[u] ; i!=-1 ; i=edge[i].next) { v=edge[i].to; if(v==pre) continue ; if(!dnf[v]) { Tarjan(v,u) ; low[u]=min(low[u],low[v]) ; if(dnf[u] < low[v]) //一条桥 { bridge++; edge[i].ok=true ; edge[i^1].ok=true ; } } else if(Instack[v] ) low[u]=min(low[u],dnf[v]); } if(low[u]==dnf[u]) //求得一个连通分量 { sum++; do { v=Stack[--top]; Instack[v]=false; belong[v]=sum; }while(u!=v) ; } } void lca(int u ,int v) { if(dep[u] > dep[v]) swap(u,v); //统一 u的高度大于v ; while(dep[u] < dep[v]) //从v向上找,直到和u在同一高度 { if(a[v]) //边找边修改 { bridge--; a[v]=0; } v=father[v]; } while(u!=v) //继续找,直到u=v ; { if(a[u]) { bridge--; a[u]=0; } if(a[v]) { bridge--; a[v]=0; } u=father[u]; v=father[v]; } } void lca_dfs(int root) { queue<int>q; memset(dep,-1,sizeof(dep)); dep[root]=0; //数的层次 a[root]=1; //标记root点, father[root]=-1; //父亲节点 q.push(root); while(!q.empty()) { int u=q.front(); q.pop(); for(int i = 0 ; i < g[u].size() ; i++) { int v=g[u][i]; if(dep[v]!= -1) continue ; dep[v] = dep[u]+1 ; a[v]=1; father[v]=u; q.push(v); } } } void solve(int n) { memset(dnf,0,sizeof(dnf)); memset(Instack,false,sizeof(Instack)); bridge=top=dfsn=sum=0; Tarjan(1,1) ; //求连通分量和桥 for(int i = 1 ; i <= sum ; i++) g[i].clear(); for(int u = 1 ; u <=n ; u++) for(int j = head[u] ; j!=-1 ; j=edge[j].next) if(edge[j].ok) { int v = edge[j].to ; g[belong[u]].push_back(belong[v]) ; //缩点建边 g[belong[v]].push_back(belong[u]) ; } lca_dfs(1); //把简化后的图,弄成一颗层次的树 ; int Q,u,v; scanf("%d",&Q); while(Q--) { scanf("%d%d",&u,&v); lca( belong[u], belong[v]) ; //给图加上一条边 printf("%d\n",bridge); } } int main() { int n , m , u,v,k=0 ; while(~scanf("%d%d",&n,&m)) { if(n==0 &&m==0) break; top=0; memset(head,-1,sizeof(head)); while(m--) { scanf("%d%d",&u,&v); add(u,v); //无向图 ; add(v,u) ; } printf("Case %d:\n",++k); solve(n); } return 0; }