题目链接:
题意:
给出一个N(1 ≤ N ≤ 100,000)个点 和 M(N - 1 ≤ M ≤ 200,000)的连通图.
有Q ( 1 ≤ Q ≤ 1,000)个询问 每次询问增加一条边(累加下去)
输出每增加一条边后剩下的桥的数量
题解:
10W点加1000次询问 每次询问都用Tarjin算法求一次肯定会超时的
考虑 每次加一条边a-b的实质:
从a到b的路径中的所有割边都将消失
那如何记录这条路径呢
在Tarjan算法递归的过程中通过树边建立一棵树
然后每次询问分别从a b开始 一直到它们的最近公共祖先 出现的割边全部消失即可
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define maxn 100050
using namespace std;
struct node{
int to,next;
}edge[maxn*4];
int head[maxn];
int s;
int dfn[maxn],low[maxn],num;
int level[maxn],fa[maxn];
int flag[maxn];
int n,ans;
void init()
{
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(flag,0,sizeof(flag));
s=ans=num=0;
level[0]=0;
for(int i=1;i<=n;i++)
fa[i]=i;
}
void addedge(int a,int b)
{
edge[s]={b,head[a]};
head[a]=s++;
}
void lca(int a,int b)
{
while(level[b]>level[a])
{
if(flag[b])
ans--,flag[b]=0;
b=fa[b];
}
while(level[a]>level[b])
{
if(flag[a])
ans--,flag[a]=0;
a=fa[a];
}
while(a!=b)
{
if(flag[a])
ans--,flag[a]=0;
if(flag[b])
ans--,flag[b]=0;
a=fa[a],b=fa[b];
}
}
void Tarjan(int u,int pre)
{
dfn[u]=low[u]=++num;
level[u]=level[pre]+1; //建树
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(!dfn[v])
{
fa[v]=u;
Tarjan(v,u);
low[u]=min(low[u],low[v]);
if(dfn[u]<low[v]) //标记割边
flag[v]=1,ans++;
}
if(v!=pre)
low[u]=min(low[u],dfn[v]);
}
}
int main()
{
int m,a,b;
int Case=1;
while(scanf("%d%d",&n,&m)&&(n+m))
{
init();
while(m--)
{
scanf("%d%d",&a,&b);
addedge(a,b);
addedge(b,a);
}
Tarjan(1,0);
scanf("%d",&m);
printf("Case %d:\n",Case++);
while(m--)
{
scanf("%d%d",&a,&b);
if(ans!=0)
lca(a,b);
printf("%d\n",ans);
}
printf("\n");
}
return 0;
}