输入:
3 2
1 2
2 3
2
1 2
1 3
题意就是先输入n,m 表示一颗树有n个节点,m条无向边。然后又q次操作, 每一次操作 输入i,j 表示在节点i和节点j之间加一条边,并且询问在加边之后的图中存在多少个桥。
在这里先感谢大牛:
http://www.cnblogs.com/scau20110726/archive/2013/06/14/3135095.html
他的博客真的给了我很多的帮助,在我犯了很多很多错误的时候 用很简短很清晰的代码拨正了我的错误思维。膜一发
这题 我一开始是这样写的: 先dfs 跑一发tarjan,把dfn,low 桥 连通块 都跑出来。 然后再 dfs 跑一发st的lca在线询问, 然后对于加边 i,j 我们就从i 向上遍历到 lca,从j 向上遍历到lca,扫描减少的桥的数量。 不知道是不是姿势写的太丑,还是两遍dfs耗时间,反正就是T了。 (现在想想 好像是因为 fa[i] 写的有问题才会T)
然后膜了一下别人的代码, 感觉至少比我的代码少 100 行 TUT,我好菜啊
感觉自己没有学懂tarjan啊,好菜啊,明明一遍dfs就可以做完,因为我们后面lca 用很朴素的想法的话用dfn和low就可以完成了,话不多说 上代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <typeinfo>
#include <fstream>
#include <map>
#include <stack>
typedef long long ll;
using namespace std;
const int N = 100010;//点数
const int MAXM = 200010;//边数,因为是无向图,所以这个值要*2
int head[N],dfn[N],low[N],vis[N],fa[N],dcnt,bridge;
int isbri[N];
struct Edge{
int u,v,next;
int used; //判断是否为桥
int bri;
}edge[2*MAXM];
int tot;
void addedge(int u,int v){
edge[tot].v=v;
edge[tot].used=0;
edge[tot].next=head[u];
head[u]=tot++;
}
void dfs(int u,int pre){
// printf("u=%d\n",u);
vis[u]=1;
dfn[u]=low[u]=++dcnt;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
// if(v==pre) continue;
if(!edge[i].used){
edge[i].used=edge[i^1].used=1;
if(!vis[v]){
fa[v]=u;
dfs(v,u);
if( low[u] > low[v] ) low[u] = low[v];
if(low[v] > dfn[u]){ //桥
bridge++;
isbri[v]=1; // 精妙: 只会设置每一条路的终点,反向并不会设置
}
}
else if(vis[u] && low[u]> dfn[v])
low[u]=dfn[v];
}
}
}
void LCA(int u,int v){ //重点来了, 其实LCA完全没必要再扫一遍,借助low 和 dfn 完全够实现lca
if(dfn[u] < dfn[v]) swap(u,v); // dfn 即 深度
while(dfn[u] > dfn[v]){ //因为 u是较深的那个点,先尽量往上移动
if(isbri[u]) bridge--;
isbri[u]=0;
u=fa[u];
}
while(dfn[v]>dfn[u]){
if(isbri[v]) bridge--;
isbri[v]=0;
v=fa[v];
}
while(u!=v){
if(isbri[u]) bridge--;
if(isbri[v]) bridge--;
isbri[u]=isbri[v]=0;
u=fa[u];
v=fa[v];
}
//最后 u=v , 这个u就是lca,但我不说
//讲道理 复杂度有点高啊
}
bool flag[N];
void init(){
tot=0;
memset(vis,0,sizeof(vis));
memset(head,-1,sizeof(head));
memset(isbri,0,sizeof(isbri));
memset(flag,0,sizeof(flag));
dcnt = bridge = 0;
}
int main(){
// freopen("1.txt","r",stdin);
int n,m,cas=0;
while(~scanf("%d %d",&n,&m) && (n||m)){
init();
printf("Case %d:\n",++cas);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v);
addedge(v,u);
flag[v]=true;
}
dfs(1,1);
int q;
// printf("bri=%d\n",bridge);
// for(int i=1;i<=n;i++){
// printf("%d ",dfn[i]);
// }
// cout<<endl;
scanf("%d",&q);
while(q--){
int a,b;
scanf("%d %d",&a,&b);
if(bridge==0){
printf("0\n");
continue;
}
LCA(a,b);
printf("%d\n",bridge);
}
}
return 0;
}