POJ3694(求割边+LCA)

题意:给出一副图,有Q个询问,每次询问添加一条边,问添加该边后还有多少个割边。

题解:先用tarjan缩点求出总的割变数,每次询问后再进行缩点,缩点过程中计算减少的割边数。


代码:

#include 
#include 
#include 
#include 
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  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;
}

你可能感兴趣的:(tarjan)