题意:给出一副图,有Q个询问,每次询问添加一条边,问添加该边后还有多少个割边。
题解:先用tarjan缩点求出总的割变数,每次询问后再进行缩点,缩点过程中计算减少的割边数。
代码:
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <string.h>
using namespace std;
const int maxn = 100005;
int n, m, q, cnt, ans;
int pre[maxn],fa[maxn],dfn[maxn],low[maxn];
bool vis[maxn];
vector <int> G[maxn];
int find(int x)
{
return pre[x] == x ? pre[x] : pre[x] = find(pre[x]);
}
int Union(int u,int v)
{
int a = find(u);
int b = find(v);
if (a == b) return false;
pre[a] = b;
return true;
}
void tarjan(int u,int father)
{
vis[u] = 1;
dfn[u] = low[u] = ++cnt;
int v;
for (int i = 0; i < G[u].size(); i++)
{
v = G[u][i];
if (!vis[v])
{
fa[v] = u;
tarjan(v,u);
low[u] = min(low[u],low[v]);
if (low[v] > dfn[u]) ++ans;//ans是桥的数量
else Union(v,u);//不是桥的部分进行缩点
}
else if (v != fa[u])
{
low[u] = min(low[u],dfn[v]);
}
}
}
void lca(int u,int v)
{
if (dfn[v] < dfn[u]) swap(u,v);
while (dfn[v] > dfn[u])
{
if (Union(v,fa[v])) ans--;//如果新建的桥连接的点已经缩点了,该桥就等于没用,否则割桥就减少一个。
v = fa[v];//新建的桥有可能一次性缩好几个点,比如1->2->3->4->5,此时连接1->5那么就同时把2 3 4都给缩了。
}
while (v != u)//考虑这种情况,A->B,A->C->D->E,此时连接B->E,经过上面的while循环后u = B,v = A,dfn[v] = 1,dfn[u] = 2;此时还可以缩点,可以缩A-B,所以这里要从u开始回溯。直到u和v是一个点为止。
{
if (Union(u,fa[u])) ans--;
u = fa[u];
}
}
void init()
{
cnt = ans = 0;
for (int i = 0; i < maxn; i++)
{
fa[i] = dfn[i] = vis[i] = 0;
pre[i] = i;
G[i].clear();
}
}
int main()
{
int u,v,kase = 0;
while (scanf("%d%d",&n,&m) != EOF)
{
if (n == 0 && m == 0) break;
init();++kase;
for (int i = 0; i < m; i++)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
ans = 0;
fa[1] = 1;
tarjan(1,1);//tarjan初始化缩点
scanf("%d",&q);
printf("Case %d:\n",kase);
for (int i = 0; i < q; i++)
{
scanf("%d%d",&u,&v);
lca(u,v);
printf("%d\n",ans);
}
puts("");
}
return 0;
}