HDU-3749 Financial Crisis(并查集+点双连通分量)

题意:

给一个(保证输入无重边,无自环)无向图N个点M条边,之后有Q条询问,每条询问为: 输入两个点,问u点与v点之间有几条(除了首尾两点外,其他点不重复)的路径.如果有0条或1条输出zero或one,如果有2条以上,输出”two or more”.


思路:

并查集+点双连通分量,先用并查集判断两点是否连通,否输出zero,是再进行判断两个点是否是属于同一双连通分量,不属于肯定是one,属于的话再判断该双连通分量的点的个数,<=2则是one条边,否则为two or more。

代码实现不难,注意细节就好。时间复杂度为O(M+Q*N);


代码:

#include <iostream>
#include <string.h>
#include <vector>
#include <cstdio>
#include <stack>
#include <map>
using namespace std;
const int maxn = 5005;
const int maxm = 20005;
struct node
{
	int u, v, next;
} edge[maxm];
int no, head[maxn];
int n, m, q;
int F[maxn];
int num[maxn], low[maxn], index;
int iscut[maxn];	//判断是否为割点
int bccno[maxn], bcccnt;
stack<node> S;
vector<int> bcc[maxn];
map<int, int> cutno[maxn];	//记录割点属于哪几个点双连通分量
map<int, int>::iterator it;
inline void init()
{
	no = 0;
	memset(head, -1, sizeof head);
	for(int i = 0; i < n; ++i) F[i] = i, cutno[i].clear(), bcc[i].clear();
	bcccnt = 0;
	while(!S.empty()) S.pop();
	memset(iscut, 0, sizeof iscut);
	index = 0;
	memset(num, 0, sizeof num);
	memset(bccno, 0, sizeof bccno);
}
inline void add(int u, int v)
{
	edge[no].u = u; edge[no].v = v; 
	edge[no].next = head[u]; head[u] = no++;
	edge[no].u = v; edge[no].v = u; 
	edge[no].next = head[v]; head[v] = no++;
}
int GetF(int x)
{
	if(x == F[x]) return x;
	F[x] = GetF(F[x]);
	return F[x];
}
void merge(int a, int b)
{
	int fa = GetF(a), fb = GetF(b);
	if(fa != fb) F[fb] = fa;
}
void tarjan(int cur, int father)
{
	int child = 0;
	num[cur] = low[cur] = ++index;
	for(int k = head[cur]; k != -1; k = edge[k].next)
	{
		if(!num[edge[k].v])
		{
			S.push(edge[k]);
			++child;
			tarjan(edge[k].v, cur);
			low[cur] = min(low[cur], low[edge[k].v]);
			if(low[edge[k].v] >= num[cur])
			{
				iscut[cur] = 1;
				++bcccnt;
				cutno[cur].insert(make_pair(bcccnt, 1));
				bcc[bcccnt].clear();
				while(true)
				{
					node top = S.top(); S.pop();
					if(bccno[top.u] != bcccnt)
					{
						bcc[bcccnt].push_back(top.u);
						bccno[top.u] = bcccnt;
					}
					if(bccno[top.v] != bcccnt)
					{
						bcc[bcccnt].push_back(top.v);
						bccno[top.v] = bcccnt;
					}
					if(top.u == edge[k].u && top.v == edge[k].v) break;
				}
			}
		}
		else if(edge[k].v != father && num[edge[k].v] < num[cur])
		{
			S.push(edge[k]);
			low[cur] = min(low[cur], num[edge[k].v]);
		}
	}
	if(father < 0)
	{
		if(child > 1) iscut[cur] = 1;
		else iscut[cur] = 0;
	}
}
int main()
{
	int count = 0, u, v;
	while(scanf("%d %d %d", &n, &m, &q) && (n||m||q))
	{
		init();
		for(int i = 1; i <= m; ++i)
		{
			scanf("%d %d", &u, &v);
			merge(u, v);
			add(u, v);
		}
		for(int i = 0; i < n; ++i) if(!num[i]) tarjan(i, -1);
		printf("Case %d:\n", ++count);
		for(int _ = 1; _ <= q; ++_)
		{
			scanf("%d %d", &u, &v);
			if(GetF(u) != GetF(v)) puts("zero");
			else
			{
				if(bccno[u] == bccno[v])
				{
					if(bcc[bccno[u]].size() > 2) puts("two or more");
					else puts("one");
					continue;
				}
				if(iscut[u] && iscut[v])
				{
					int flag = 1;
					for(it = cutno[u].begin(); it != cutno[u].end() && flag; ++it)
					if(cutno[v].count(it->first))
					{
						if(bcc[it->first].size() > 2) puts("two or more");
						else puts("one");
						flag = 0;
					}
					if(flag) puts("one"); 
					continue;
				}
				if(iscut[u])
				{
					if(cutno[u].count(bccno[v]))
					{
						if(bcc[bccno[v]].size() > 2) puts("two or more");
						else puts("one");
					}
					else puts("one"); 
					continue;
				}
				if(iscut[v])
				{
					if(cutno[v].count(bccno[u]))
					{
						if(bcc[bccno[u]].size() > 2) puts("two or more");
						else puts("one");
					}
					else puts("one"); 
					continue;
				}
				puts("one");
			}
		}
	}
	return 0;
}


继续加油~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值