[双连通分量] POJ 3694 Network

Network

Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 9434   Accepted: 3511

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

Source

2008 Asia Hefei Regional Contest Online by USTC

原题大意:输入点的个数N和边的个数M,接下来输入整数Q代表询问的次数,之后Q行有两个整数A,B,表示将A与B相连。问每次询问后割边(桥)的数量。

解题思路1:用tarjian找割边并标记,之后用lca遍历A与B到最近公共祖先的路径,这段路上是割边的处理掉,总数-1就可以了。

表示用链表写的,各种慢,但是POJ数据太弱了,怎么写都能过。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct list
  {
  	 int v;
  	 list *next;
  };
list *head[110010],*rear[110010];
int father[110010],times,dfn[110010],low[110010],bridge[110010],bridgenum,n;
int dep[110010];
void init()
  {
  	  int i;
  	  memset(head,0,sizeof(head));
  	  memset(dep,0,sizeof(dep));
  	  memset(rear,0,sizeof(rear));
  	  for(i=1;i<=n;++i) father[i]=i;
  	  memset(dfn,0,sizeof(dfn));
  	  memset(low,0,sizeof(low));
  	  memset(bridge,0,sizeof(bridge));
  	  times=bridgenum=0;
  }
void insert(int a,int b)
  {
  	  if(rear[a]!=NULL)
  	   	 {
  	   	    rear[a]->next=new list;
  	   	    rear[a]=rear[a]->next;
		} else head[a]=rear[a]=new list;
			rear[a]->v=b;
			rear[a]->next=NULL;
		return;
  }
void tarjian(int v)
  {
  	 bool flag=true;
  	 dfn[v]=low[v]=++times;
  	 dep[v]=dep[father[v]]+1;
  	 for(list *p=head[v];p!=NULL;p=p->next)
  	   {
  	   	  if(p->v==father[v]&&flag)
  	   	    {
  	   	       flag=false;
  	   	       continue;
			}
		  if(!dfn[p->v])
		    {
		       father[p->v]=v;
		       tarjian(p->v);
		       low[v]=min(low[v],low[p->v]);
		       if(low[p->v]>dfn[v])
		         {
		            ++bridgenum;
		            bridge[p->v]=1;
				 }
			}else  low[v]=min(low[v],dfn[p->v]);
	   }
  }
void lca(int a,int b)
  {
  	 if(dep[a]<dep[b]) swap(a,b);
  	 while(dep[a]!=dep[b])
  	  {
  	  	if(bridge[a])
  	  	  {
  	  	  	bridge[a]=0;
  	  	  	--bridgenum;
		  }
		a=father[a];
	  }
	 while(a!=b)
	  {
	  	if(bridge[a])
	  	  {
	  	  	bridge[a]=0;
	  	  	--bridgenum;
		  }
		if(bridge[b])
	  	  {
	  	  	bridge[b]=0;
	  	  	--bridgenum;
		  }
		a=father[a];
		b=father[b];
	  }
  	 return;
  }
int main()
  {
  	 int m,i,a,b,q,ccase=0;
  	 while(~scanf("%d%d",&n,&m),n&&m)
  	   {
  	   	  init();
  	   	  for(i=0;i<m;++i)
  	   	    {
  	   	      scanf("%d%d",&a,&b);
  	   	      insert(a,b);insert(b,a);
			}
		  tarjian(1);
		  scanf("%d",&q);
		  printf("Case %d:\n",++ccase);
		  for(i=1;i<=q;++i)
		    {
		       scanf("%d%d",&a,&b);
		       lca(a,b);
		       printf("%d\n",bridgenum);
			}
	   }
  	 return 0;
  }

解题思路2:在原来的基础上用并查集优化,这种做法是看了大神们的解题思路写出的。

将双连通的两个点弄成一个集合,这样在LCA时只要判断是否是一个集合,将桥的数量减去即可。

#include<stdio.h>
#include<string.h>
struct list
  {
  	 int v;
  	 list *next;
  };
list *head[111010],*rear[111010];
int n,m,father[111010],dep[110010],low[110010],fath[110010],bridgenum;
void init()
  {
  	 int i;
  	 memset(head,0,sizeof(head));
  	 memset(rear,0,sizeof(rear));
  	 memset(dep,0,sizeof(dep));
  	 memset(low,0,sizeof(low));
  	 memset(fath,0,sizeof(fath));
  	 for(i=1;i<=n;++i) father[i]=i;
  	 bridgenum=0;
  }
void insert(int a,int b)
  {
  	 if(rear[a]!=NULL)
  	   {
  	   	  rear[a]->next=new list;
  	   	  rear[a]=rear[a]->next;
	   } else head[a]=rear[a]=new list;
	 rear[a]->v=b;
	 rear[a]->next=NULL;
  }
int find(int x)
  {
  	 return father[x]==x?x:father[x]=find(father[x]);
  }
void merge(int x,int y)
  {
  	 int fx=find(x);
  	 int fy=find(y);
  	 if(fx!=fy) father[fx]=fy;
  }
void tarjian(int v,int deps)
  {
  	 bool flag=true;
  	 dep[v]=low[v]=deps;
  	 for(list *p=head[v];p!=NULL;p=p->next)
  	   {
  	   	  if(p->v==fath[v]&&flag)
  	   	    {
  	   	       flag=false;
  	   	       continue;
			}
  	   	  if(!dep[p->v])
  	   	    {
  	   	      fath[p->v]=v;
  	   	      tarjian(p->v,deps+1);
  	   	      if(low[v]>low[p->v]) low[v]=low[p->v];
  	   	      if(low[p->v]<=dep[v]) merge(p->v,v);
  	   	      else bridgenum++;
			}
		  else if(low[v]>dep[p->v]) low[v]=dep[p->v];
	   }
  }
void judge(int v)
  {
  	 int x=find(v);
  	 int y=find(fath[v]);
  	 if(x!=y)
  	   {
  	   	  --bridgenum;
  	   	  father[x]=y;
	   }
  }
void lca(int u,int v)
  {
  	 while(dep[u]>dep[v])
  	    {
  	       judge(u);
  	       u=fath[u];
		}
	  while(dep[u]<dep[v])
	    {
	       judge(v);
	       v=fath[v];
		}
	  while(u!=v)
	    {
	    	 judge(u);judge(v);
	    	 u=fath[u];v=fath[v];
		}
  }
int main()
  {
  	 int a,b,q,num=0;
  	 while(~scanf("%d%d",&n,&m))
  	   {
  	   	  if(n==0&&m==0) break;
  	   	  init();
  	   	  while(m--)
  	   	    {
  	   	      scanf("%d%d",&a,&b);
  	   	      insert(a,b);insert(b,a);
			}
		  printf("Case %d:\n",++num);
		  tarjian(1,1);
		  scanf("%d",&q);
		  while(q--)
		    {
		       scanf("%d%d",&a,&b);
		       if(find(a)!=find(b)) lca(a,b);
		       printf("%d\n",bridgenum);
			}
		  printf("\n");
	   }
  	 return 0;
  }

  

时间: 2024-10-01 03:24:53

[双连通分量] POJ 3694 Network的相关文章

Poj 3694 Network (连通图缩点+LCA+并查集)

题目链接: Poj 3694 Network 题目描述: 给出一个无向连通图,加入一系列边指定的后,问还剩下多少个桥? 解题思路: 先求出图的双连通分支,然后缩点重新建图,加入一个指定的边后,求出这条边两个端点根节点的LCA,统计其中的桥,然后把这个环中的节点加到一个集合中,根节点标记为LCA. 题目不难,坑在了数组初始化和大小 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #inclu

POJ 3694——Network——————【连通图,LCA求桥】

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 compute

poj 3694 Network (桥入门)

1 /******************************************** 2 Network(POJ 3694) 3 http://poj.org/problem?id=3694 4 桥入门题 + LCA(树中最小公共祖先) 5 做这题必须要会用邻接表(做这题才学会),因为 6 给的边有重边,用vector不好记录重边. 7 8 ********************************************/ 9 10 #include<iostream> 11

[双连通分量] POJ 3177 Redundant Paths

Redundant Paths Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 13712   Accepted: 5821 Description In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1..F) to another field, Bessie and the rest of the

POJ 3694 Network

Network Time Limit: 5000ms Memory Limit: 65536KB This problem will be judged on PKU. Original ID: 369464-bit integer IO format: %lld      Java class name: Main A network administrator manages a large network. The network consists of N computers and M

POJ 3694 Network (tarjan + LCA)

题目链接:http://poj.org/problem?id=3694 题意是给你一个无向图n个点,m条边,将m条边连接起来之后形成一个图,有Q个询问,问将u和v连接起来后图中还有多少个桥. 首先用tarjan标记点的low和dfn值,那么u和v相连的边是桥的条件是dfn[u] < low[v](说明v与u不在一个连通分量里面,v无法通过回溯到达u点,画个图模拟会清楚).那么bridge[v]++表示u与v相连的边是桥(若是标记bridge[u]++,则最后的答案可能会出错,亲测).要是u和v相

tarjan算法求桥双连通分量 POJ 3177 Redundant Paths

POJ 3177 Redundant Paths Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 12598   Accepted: 5330 Description In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1..F) to another field, Bessie and the re

poj 3694 Network(桥+lca)

给定一个无向无环图,保证连通,求每加入一条给定的边图中还剩下多少桥. 双联通缩点重新建图后,再用lca在线算法解. lca算法参考斌神http://www.cnblogs.com/kuangbin/p/3184884.html 这个版本的lca思路大致是先topsort,再用并查集分别从查询的两点向根节点回溯,直到两个点碰撞.效率我分析不出来,但看得出效率很高,每次查询都对后面查询做了工作. 代码: #include<iostream> #include<cstdio> #incl

POJ - 3694 Network(tarjan+lca)

题意:给出一个无相图,然后q次新增加边,问在添加边的过程中桥的数目当且仅当无向边(u,v)为树枝的时候,需要满足dfn(u)<low(v),也就是v向上翻不到u及其以上的点,那么u-v之间一定能够有1条或者多条边不能删去,因为他们之间有一部分无环,是桥思路:首先我们知道在给定一张图之后,不断添加边,桥的数目只会减少而不是增加tarjan的使用就是缩点,将一个连通分量缩成一个点,那么那个连通分量中的边都不会是桥同时缩完点之后我们就会发现,桥其实就是新形成的树的边在添加的过程中,如果是在连通分量内就