poj 3694 Network 边双连通
给一幅图,若干个操作,每个操作时连接两个点,对于每个操作之后的图判断图中还有几条割边
解法
先求出最初的图中有几条割边bridge
在求的过程中顺便建立一个方向,相当于变成有向图,如果发现某条边不是割边,就利用并查集将两个点合并,也可以不合并,但是速度上就相差很多了
最后,一个联通块中的点就只以一个点为根了
还有,如果s-t是割边,就标记一下t
现在要连接两个点a,b
在找他们的LCA的时候如果发现某条边是割边就bridge--
因为需要一层层上去,遍历到从a,b到LCA的每条边,所以这个过程就直接暴力网上找了,不知还有没有什么更好的方法
View Code
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int maxn=100010; struct Edge { int s,t; int next; int vis; }edge[1000005]; int head[maxn]; int E=0; void add_edge(int s,int t) { edge[E].s=s; edge[E].t=t; edge[E].vis=0; edge[E].next=head[s]; head[s]=E++; } int Time,N,M; int dfn[maxn],low[maxn]; int Top; int bridge; inline int min(int a,int b){return a<b?a:b;} int f[maxn]; bool mark[maxn]; int find(int x){return x==f[x]?x:f[x]=find(f[x]);} void dfs(int s) { int i,t; dfn[s]=low[s]=++Time; for (i=head[s];i!=-1;i=edge[i].next) { if(edge[i].vis)continue; edge[i].vis=edge[i^1].vis=1; t=edge[i].t; if (!dfn[t]) { f[t]=s; dfs(t); low[s]=min(low[s],low[t]); } else low[s]=min(low[s],dfn[t]); if(low[t]<=dfn[s]) { int x=find(t); int y=find(s); if(x!=y) f[x]=y; } if(low[t]>dfn[s]) { mark[t]=true; bridge++; } } } void SCC(int n) { int i;bridge=0;f[1]=1; memset(mark,false,sizeof(mark)); memset(dfn,0,sizeof(int)*(n+1)); for(i=1;i<=n;i++) { if(!dfn[i]) dfs(i); } } void LCA(int a,int b) { if(dfn[a]<dfn[b]) { a^=b; b^=a; a^=b; } while(dfn[a]>dfn[b]) { if(mark[a]) {bridge--;mark[a]=false;} a=f[a]; } while(a!=b) { if(mark[a]){bridge--;mark[a]=false;} a=f[a]; if(mark[b]){bridge--;mark[b]=false;} b=f[b]; } } int main() { int n,m,i,q,a,b,ca=1; while(scanf("%d%d",&n,&m),(n||m)) { E=0; memset(head,-1,sizeof(head)); for(i=0;i<m;i++) { scanf("%d%d",&a,&b); add_edge(a,b); add_edge(b,a); } SCC(n); printf("Case %d:\n",ca++); scanf("%d",&q); while(q--) { scanf("%d%d",&a,&b); LCA(a,b); printf("%d\n",bridge); } puts(""); } return 0; }