题目描述
You are to help the administrator by reporting the number of bridges in the network after each new link is added.
输入格式
Each of the following M lines contains two integers A and B ( 1≤ A ≠ B ≤ N), which indicates a link between computer A and B. Computers are numbered from 1 to N. It is guaranteed that any two computers are connected in the initial network.
The next line contains a single integer Q ( 1 ≤ Q ≤ 1,000), which is the number of new links the administrator plans to add to the network one by one.
The i-th line of the following Q lines contains two integer A and B (1 ≤ A ≠ B ≤ N), which is the i-th added new link connecting computer A and B.
The last test case is followed by a line containing two zeros.
输出格式
For each test case, print a line containing the test case number( beginning with 1) and Q lines, the i-th of which contains a integer indicating the number of bridges in the network after the first i new links are added. Print a blank line after the output for each test case.
大致翻译:给你N个点M条边的无向图,并且有Q次加边,问每次加边之后图中的桥的数量。
显然,如果加入的边的两个端点在同一个边双内,那么桥的数量不变。所以我们先用Tarjan对原图进行边双连通分量缩点得到一棵树。
接着,对于两个端点不在一个边双的情况,显然桥的数量减少量等于两个端点的树上距离。我们求出树上距离,然后把两端点之间的边标记起来,即边长由原来的1改成0。每次求树上距离时就先一个个地往上爬,顺便还可以标记边。时间复杂度为O(M+QN),可以通过本题,但显然不优。
既然边长变成0了,我们以后都没必要再管这些边了,所以我们可以用缩树的办法,用并查集把两个端点之间的点合并到一个集合中去,然后下次爬到这两个端点处时直接跳到LCA的位置就好了。
时间复杂度为O(M+Qα(N)),接近线性
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define maxn 100010
#define maxm 200010
using namespace std;
struct edge{
int to,next;
edge(){}
edge(const int &_to,const int &_next){ to=_to,next=_next; }
}e[maxm<<1];
int head[maxn],k;
int dep[maxn],pre[maxn],fa[maxn];
int dfn[maxn],low[maxn],tot,stack[maxn],top;
int uu[maxm],vv[maxm];
int n,m,t,ans;
inline void init(){
for(register int i=0;i<=n;i++) head[i]=-1,dep[i]=dfn[i]=0;
k=top=ans=tot=0;
}
inline void add(const int &u,const int &v){ e[k]=edge(v,head[u]),head[u]=k++; }
void tarjan_dcc(int u,int pre){
if(dfn[u]) return;
dfn[u]=low[u]=++tot,stack[++top]=u;
bool flag=true;
for(register int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(v==pre&&flag) { flag=false; continue; }
tarjan_dcc(v,u),low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u]){
while(stack[top]!=u) low[stack[top--]]=low[u];
top--;
}
}
inline void rebuild(){
for(register int i=0;i<=n;i++) head[i]=-1; k=0;
for(register int i=0;i<m;i++){
int u=low[uu[i]],v=low[vv[i]];
if(u!=v) add(u,v),add(v,u);
}
}
void dfs(int u,int father,int deep){
dep[u]=deep,pre[u]=u;
for(register int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(v!=father) dfs(v,u,deep+1),fa[v]=u,ans++;
}
}
int get(int x){
if(x!=pre[x]) pre[x]=get(pre[x]);
return pre[x];
}
inline void getlca(int u,int v){
u=get(u),v=get(v),top=0;
while(u!=v){
if(dep[u]>dep[v]) stack[++top]=u,u=get(fa[u]);
else stack[++top]=v,v=get(fa[v]);
ans--;
}
while(top) pre[stack[top--]]=u;
}
int main(){
int T=0;
while(~scanf("%d %d",&n,&m),n+m){
init();
for(register int i=0;i<m;i++){
scanf("%d %d",&uu[i],&vv[i]);
add(uu[i],vv[i]),add(vv[i],uu[i]);
}
tarjan_dcc(1,-1),rebuild();
dfs(low[1],-1,1);
scanf("%d",&t);
printf("Case %d:\n",++T);
while(t--){
int u,v; scanf("%d %d",&u,&v);
getlca(low[u],low[v]);
printf("%d\n",ans);
}
printf("\n");
}
return 0;
}