[POJ3694]网络
题目描述
一位网络管理员管理着一家网吧。该网吧有n台计算机和m条网线连接着。任何两台计算机都是直接或者间接相连,因此数据可以在任意两台计算机之间传输。
管理员发现一些网线是相当重要的,因为他们中间的任何一条网线发生故障将会影响到一些计算机之间数据的传输,他称这样的网线为桥,现在打算添加一些新的网线来排除一些桥。
每当添加一条新的网线后,请你告诉他目前网络中还有多少桥。
输入格式
输入文件包含多组测试数据
每组测试数据的第一行包含两个整数N(1≤N≤100,000)和M(N-1≤M≤200,000)
接下来m行,每行两个整数A和B (1≤A≠B≤N),表示计算机A和B之间有一条网线相连,计算机从1到n编号。保证任何两台计算机最初都是连接着的。
接下来一行包含一个整数Q (1≤Q≤1,000),表示管理员准备新添加网线的数目
接下来Q行每行两个数A和B (1<=A ≠B<= N),表示计算机A和B之间将添加一条网线,最后一行为“0 0”作为文件的结束。
输出格式
对于每组数据输出Q行,第i行表示第i条网线添加后,网络中桥的数目。每组数据结束后输出一个空行。
样例一
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
output
Case 1:
1
0
Case 2:
2
0
题解:
很明显就可以看出来这道题要用tarjan求割边。
但这题会加边,所以我们就先用并查集把不是割边的点合并起来,是割边就记答案。
然后我们知道tarjan出来的图必定是一棵树,每个点dfn可以看做是这个点的深度。
于是新加一条边就等于在tarjan图上x->y这条路所有点并到一起,如果发现两个点不属于一个父亲,则答案减一。
#include<bits/stdc++.h> using namespace std; const int N=100005; int n,m,dfn[N],lk[N],f[N],fa[N],sign,ans,T; vector<int>e[N]; int F(int x){return x==f[x]?x:f[x]=F(f[x]);} bool Union(int x,int y){ x=F(x),y=F(y); if(x==y)return 0; return f[x]=y,1; } void tarjan(int x){//求割边 dfn[x]=lk[x]=++sign; int i,y; for(i=0;i<e[x].size();i++){ y=e[x][i]; if(y==fa[x])continue; if(!dfn[y]){ fa[y]=x; tarjan(y); lk[x]=min(lk[x],lk[y]); if(lk[y]>dfn[x])ans++; else Union(x,y); } else lk[x]=min(lk[x],dfn[y]); } } void lca(int x,int y){//tarjan图上lca if(dfn[x]>dfn[y])swap(x,y); while(dfn[y]>dfn[x]){if(Union(y,fa[y]))ans--;y=fa[y];} while(x^y){if(Union(x,fa[x]))ans--;x=fa[x];} } int main() { int i,x,y; while(1){ scanf("%d%d",&n,&m); if(!n)break; for(i=1;i<=n;i++)e[i].clear(),f[i]=i; for(i=1;i<=m;i++){ scanf("%d%d",&x,&y); e[x].push_back(y),e[y].push_back(x); } memset(dfn,0,sizeof(dfn)); ans=sign=0; tarjan(1); printf("Case %d:\n",++T); scanf("%d",&m); for(i=1;i<=m;i++){ scanf("%d%d",&x,&y); lca(x,y); printf("%d\n",ans); } printf("\n"); } }

浙公网安备 33010602011771号