http://poj.org/problem?id=3694
用了Tarjan bfs 缩点
所以时间复杂度比较高
思路 先建双向图 重边要处理(用一个变量表示边数)
用Tarjan算法缩点
重新建了一个缩点后的双向图
增加边时用bfs 搜索路径上的桥如果还有就对数量进行操作并将此桥标记为没有
每次重复
#include<iostream> #include<cstring> #include<stack> #include<cstdio> #include<queue> using namespace std; const int N=100005; struct node { struct tt *next; }mem[N];//原图 struct nodeq { struct ttq *next; }memq[N]; struct tt { struct tt *next; int j; int k; }; struct ttq { struct ttq *next; int j; bool bridge;//此桥是否存在 };//缩点后的图 void build(int i,int j) { struct tt *t; t=mem[i].next; while(t!=NULL) { if(t->j==j) { ++t->k;//记录此路的数量 return ; } t=t->next; } t=new tt; t->j=j; t->k=1; t->next=mem[i].next; mem[i].next=t; } bool in[N]; int low[N]; int dfn[N]; stack<int>str; int time; bool visited[N]; int uppoint[N];//缩点数组 int f[N]; int l,r; int ans; void Clear(int n) { for(int i=1;i<=n;++i) {mem[i].next=NULL;memq[i].next=NULL;} } void Tarjan(int pre,int k,int x) { ++time; visited[x]=true; in[x]=true; dfn[x]=low[x]=time; str.push(x); struct tt *t=mem[x].next; while(t!=NULL) { if(visited[t->j]==false) { Tarjan(x,t->k,t->j); low[x]=min(low[x],low[t->j]); } else if(in[t->j]==true&&(pre!=t->j||(pre==t->j&&k>1)))//如果搜到上一个点则必须是不只一条路 { low[x]=min(low[x],dfn[t->j]); } t=t->next; } if(low[x]==dfn[x]) { while(str.top()!=x) { uppoint[str.top()]=x;//缩点 全缩成x in[str.top()]=false; str.pop(); } uppoint[x]=x; in[str.top()]=false; str.pop(); } } void buildtree(int i,int j)//建新图 { struct ttq *t=new ttq; t->j=j; t->bridge=true;//初始化存在 t->next=memq[i].next; memq[i].next=t; } void dfs(int x) { visited[x]=true; struct tt *t=mem[x].next; while(t!=NULL) { if(!visited[t->j]) { if(uppoint[x]!=uppoint[t->j]) { ++ans; buildtree(uppoint[x],uppoint[t->j]);//双向 buildtree(uppoint[t->j],uppoint[x]);//双向 } dfs(t->j); } t=t->next; } } void rebuild() { memset(visited,false,sizeof(visited)); dfs(1); } int dec(int st,int nd) { queue<int>str; memset(visited,false,sizeof(visited)); str.push(st); f[st]=st; visited[st]=true; struct ttq *t; while(1)//bfs 找两点路径 { int x=str.front(); if(x==nd){break;} str.pop(); t=memq[x].next; while(t!=NULL) { if(!visited[t->j]) { visited[t->j]=true; f[t->j]=x; str.push(t->j); } t=t->next; } } int k=nd; int num=0; while(k!=st) { int pre=f[k]; t=memq[pre].next; while(t!=NULL) { if(t->j==k) { if(t->bridge==true) { ++num;t->bridge=false;//如果此桥存在 则计数并标记 } break; } t=t->next; } t=memq[k].next; while(t!=NULL)//反向的也要 { if(t->j==pre) { if(t->bridge==true) { ++num;t->bridge=false; } break; } t=t->next; } k=pre; } return num/2;//正向加反向的 所以要除2 } int main() { int n,m; for(int w=1;;++w) { scanf("%d %d",&n,&m); if(n==0&&m==0) break; while(m--) { int i,j; scanf("%d %d",&i,&j); build(i,j); build(j,i); } memset(in,false,sizeof(in)); memset(visited,false,sizeof(visited)); while(!str.empty()) str.pop(); time=0; Tarjan(1,1,1); ans=0; rebuild(); int q; scanf("%d",&q); printf("Case %d:\n",w); while(q--) { scanf("%d %d",&l,&r); l=uppoint[l];r=uppoint[r]; if(ans!=0&&l!=r) { ans-=dec(l,r); } printf("%d\n",ans); } printf("\n"); Clear(n); } return 0; }