题意:
给一个(保证输入无重边,无自环)无向图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;
}
继续加油~