题目源: POJ3694 Network
题意:给你一个图,有Q个操作,每个操作将点u和v之间连一条边,问你在每次加边之后图中的割边的数量。
分析:首先用Tarjan在加边前求一次割边的数量,并用并查集缩点,缩点之后的图将会变成一棵树。加边时,任意两点之间直接暴力求LCA,在求LCA的过程更新割边的数量。因为一棵树加上一条边之后必定有环,那么就割边的数量就会减少。具体实现看代码。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100005
using namespace std;
struct node
{
int v,next;
}edge[N<<2];
int pre[N],cnt;
int dfn[N],low[N],id;
int fa[N],f[N];
int n,m,q,cut;
int Scan()
{
int f = 1,res = 0;
char ch;
while((ch = getchar())==' ' || ch == '\n');
if(ch == '-')f = -1;
while(ch >= '0' && ch <= '9')
{
res = res * 10 + ch - '0';
ch = getchar();
}
return f * res;
}
void init()
{
cnt = id = cut = 0;
memset(pre, -1, sizeof(pre));
memset(dfn, 0, sizeof(dfn));
for(int i = 0; i <= n; ++i)
f[i] = i;
}
void addEdge(int u, int v)
{
edge[cnt].v = v;
edge[cnt].next = pre[u];
pre[u] = cnt++;
}
int _find(int x)
{
int xx = x;
while(x != f[x])
x = f[x];
while(xx != x)
{
int u = f[xx];
f[xx] = x;
xx = u;
}
return x;
}
void _union(int x, int y)
{
f[_find(y)] = _find(x);
}
void Tarjan(int u)
{
dfn[u] = low[u] = ++id;
for(int i = pre[u]; ~i; i=edge[i].next)
{
int v = edge[i].v;
if(!dfn[v])
{
fa[v] = u;
Tarjan(v);
if(low[v] > dfn[u])
cut++;
else _union(u, v);
low[u] = min(low[u], low[v]);
}
else if(v != fa[u])
low[u] = min(low[u], dfn[v]);
}
}
void LCA(int u, int v)
{
if(dfn[u] > dfn[v])
swap(u, v);
int x = _find(u);
while(dfn[v] > dfn[u])
{
int y = _find(v);
if(x != y)cut--;
f[y] = x;
v = fa[v];
}
int y = _find(v);
while(u != v)
{
int x = _find(u);
if(y != x)cut--;
f[x] = y;
u = fa[u];
}
printf("%d\n", cut);
}
int main()
{
int cas = 0;
while(~scanf("%d%d", &n,&m), n&&m)
{
init();
int u,v;
while(m--)
{
u = Scan(),v = Scan();
addEdge(u, v);
addEdge(v, u);
}
Tarjan(1);
printf("Case %d:\n", ++cas);
q = Scan();
while(q--)
{
u = Scan(),v = Scan();
LCA(u, v);
}
printf("\n");
}
return 0;
}