题意:
给你n个点和m条边,然后会有q次添加边,问你添加上当前边之后,剩下的桥的个数是多少。
题解:
求割边+LCA,今天看了一整天的博客,都没想着怎么回事,为什么要用LCA,为什么求割边之后就成为一棵树了,终于在晚上想通了。之前一直在想为什么要用LCA来做这道题,原来他们缩点(缩点之后,那些缩点机会里面的边相互联通)之后会形成一棵树,然后因为已经经过缩点了,所以这些树上的边都是桥(终于理解为什么他们说缩点之后的树边为桥了),那么如果加入的这条边是属于一个缩点的话(缩点里面的点算是一个集合)那么就对原图中的桥没有任何影响,但是如果加入的边是属于两个缩点的话,那么就会形成一个环,那么任意删除这个环里面的一条边,这棵树还是互通的。ORZ终于理解了,那么就可以利用LCA的特性去算出到底减少了多少条桥了,因为是最近公共祖先,那么新加入的这条边的两个点通过LCA找到对方肯定是走最短的路径(在树上走最小的边)那么就可以得到结果了,总桥数减去走LCA上的边就是题目要的答案了!!!!哈哈我终于想通了!
注意是求每次增加之后的剩余的桥数,不是在原图基础上只是加这条,而是要考虑之前连了的边,每次回答是在上一次的基础之上)
但是尴尬的是,有一些博客的代码有一些数据过不了,害我又找了一个晚上,差点通宵。数据的话看看我的代码,如果是我代码中提到的那种情况的话,可以去试一下。
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN=100000+7;
vector<int>map[MAXN];
int dfn[MAXN];
int low[MAXN];
int fa[MAXN];
bool vis[MAXN];
int bridge[MAXN];
int Index;
int n,m,ans;
void init()
{
memset(vis,false,sizeof(vis));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(bridge,0,sizeof(bridge));
for(int i=1;i<=n;i++)
fa[i]=i,map[i].clear();
Index=0;
ans=0;
}
void Tarjan(int u,int pre)
{
vis[u]=true;
dfn[u]=low[u]=++Index;
for(int i=0;i<map[u].size();i++)
{
int v=map[u][i];
if(!dfn[v])
{
fa[v]=u;
Tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u])
{
ans++;
bridge[v]=1;
}
}
else if(pre!=v)
low[u]=min(low[u],dfn[v]);
}
}
void lca(int u,int v)
{
/* if(dfn[v]<dfn[u])//如果不弄这一步的话,就要分两种情况,一种是dfn[u]>dfn[v],或者是dfn[v]>dfn[u]的情况。
swap(u,v); //但是事实证明有这一步会出现错误,不相信的话可以测试下面这组数据
while(dfn[v]>dfn[u])
{
if(bridge[v]) ans--,bridge[v]=0;
v=fa[v];
}*/
/*
6 5
1 2
2 4
2 3
3 5
4 6
1
5 6
这种数据的答案应该是
Case 1:
1
或者换成
6 5
1 2
2 3
2 4
3 5
4 6
1
5 6
*/
//还是分别操作两次好点。不然的话就有可能出现一个找到了最近公共祖先,两者不相等,找到祖先的那个再找父亲的话就会导致超过的最近公共祖先了,导致出现错误,应该两个都找到缩点先。
while(u!=v)
{
while(dfn[v]>dfn[u])//这样做相当于找到强连通分量(类似于找到一个所有点相互联通的集合)(类似于找到一个缩点为止,只是我这里没写缩点,类似于缩点是思想,因为缩点里的点任意联通)
{
if(bridge[v]) ans--,bridge[v]=0;
v=fa[v];
}
while(dfn[v]<dfn[u])
{
if(bridge[u]) ans--,bridge[u]=0;
u=fa[u];
}
// printf("%d %d\n",u,v);
// printf("fa %d %d\n",fa[u],fa[v]);
}
}
int main()
{
int k=1;
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0)
break;
init();
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
map[u].push_back(v);
map[v].push_back(u);
}
ans=0;
Tarjan(1,1);
int q;
scanf("%d",&q);
printf("Case %d:\n",k++);
for(int i=1;i<=q;i++)
{
int u,v;
scanf("%d%d",&u,&v);
lca(u,v);
printf("%d\n",ans);
}
}
}